zoukankan      html  css  js  c++  java
  • 最长M字段和(1052 1115 1053)

    这三个版本最大的区别就是数据范围的区别:N<=5000时,用n^2的dp可以过;当n达到50000时,用nlogn的dp可以过。

     

    51nod 1052:

    设dp[i][j]表示前j个数分成i段的最大字段和,转移方程由:dp[i][j]=max(max(dp[i-1][k](k=1...j-1)),dp[i][j])+a[j]);

    由于空间会超限,可采用滚动数组;用f[j]数组记录第i-1的前j的最大字段和。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=6e3+100;
    ll dp[N][2];
    ll f1[N],f2[N];
    int a[N];
    int main()
    {
        //freopen("1.txt","r",stdin);
        int n,m;
        cin>>n>>m;
        int res=0;
        ll ans=0,sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]>0)
            {
                res++;
                sum+=a[i];
            }
        }
        if(m>=res)
        {
            printf("%lld
    ",sum);
            return 0;
        }
        for(int i=1;i<=m;i++)
        {
           // memset(f1,0,sizeof f1);
            for(int j=1;j<=n;j++)
            {
                dp[i][j&1]=max(f2[j-1],dp[i][(j-1)&1])+a[j];
                f1[j]=max(f1[j-1],dp[i][j&1]);
                if(i==m)
                    ans=max(ans,dp[i][j&1]);
            }
            memcpy(f2,f1,sizeof f1);
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    51nod 1053:

    我们考虑先把原数组的相同正负性的数合并,原数组变成正负相间的数,要求只包含m个字段的最大数值和,等价于将数组中的正数连通块变为m个的最大收益;

    有两个操作可以让整数连通块减少:1.将两个正数联通块与中间的负数连通块合并,答案减去负数的绝对值。

    2.减去一个正数连通块,贡献减去正数连通块的绝对值。

    因此,无论哪个操作都是减去联通块的绝对值,我们考虑将每个连通块以绝对值为贡献排序,尽量用绝对值小的减去正数连通块的数量,这样一定是最优的。

    为了方便合并,我们采用双向链表进行操作。注意:当负数连通块在链表两端时,操作无效。

    为了方便操作,采用set方便配合链表进行删除。

    #define ll long long
    #define x first
    #define y second
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+100;
    ll sum[N], a[N];
    int l[N],r[N];
    void modify(int u)//删去链表某个点
    {
        int l1=l[u],rr=r[u];
        if(l1)
            r[l1]=rr;
        if(rr)
            l[rr]=l1;
    }
    int main()
    {
        //freopen("1.txt","r",stdin);
        int n,m;
        int cnt=0;
        cin>>n>>m;
        ll ans=0;
        int res=0;
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            if(x)
             a[++cnt]=x;
             if(x>0)ans+=x,res++;
        }
        if(m>=res)
        {
            cout<<ans<<endl;
            return 0;
        }
        res=0;
        ll u=0;
        n=cnt;
        cnt=0;
        for(int i=1,j;i<=n;i++)
        {
            j=i;
            u=a[i];
            while(j+1<=n&&a[j]*a[j+1]>0)
            {
                u+=a[j+1];
                j++;
            }
            if(!cnt&&u<0)continue;
            if(u<0&&i==n)continue;
            if(u>0)res++;
            sum[++cnt]=u;
            i=j;
        }
        for(int i=2;i<=cnt;i++)
        {
            l[i]=i-1;
            r[i-1]=i;
        }
        set<pair<ll,int> >s;
        for(int i=1;i<=cnt;i++)
        {
            s.insert({abs(sum[i]),i});
            //cout<<sum[i]<<endl;
        }
        //cout<<ans<<endl;
        for(int i=res;i>m;)
        {
            int id=s.begin()->second;
            s.erase(s.begin());
            if(sum[id]<0&&(!l[id]||!r[id]))continue;//当负数连通块在两端时无效
            //cout<<sum[id]<<endl;
            ans-=abs(sum[id]);//减去代价
            sum[id]+=sum[l[id]]+sum[r[id]];
            if(l[id])
            {
                s.erase({abs(sum[l[id]]),l[id]});
                modify(l[id]);
            }
            if(r[id])
            {
                s.erase({abs(sum[r[id]]),r[id]});
                modify(r[id]);
            }
            if(sum[id])
            s.insert({abs(sum[id]),id});
            i--;
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    51nod 1115

    这题与上题的唯一区别就是变成了环形操作。

    将双向链表变为环形双向链表,初始的首末连通块如果正负性相同则需合并。其余与上题相同。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 100010;
    int n,m,now;
    ll sum[N];
    ll a[N];
    int l[N],r[N];
    int main()
    {
        //freopen("2.txt","r",stdin);
        cin>>n>>m;
        int p=0;
        int res=0;
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            ll x;
            scanf("%lld",&x);
            if(x!=0)
                a[++p]=x;
                if(x>0)
                {
                    res++;
                    ans+=x;
                }
        }
        if(m>=res)
        {
            cout<<ans<<endl;
            return 0;
        }
        n=p;
        int cnt=0;
        p=0;
        res=0;
        ll u=0;
        for(int i=1,j;i<=n;i++)
        {
            j=i;
            u=a[i];
            while(j+1<=n&&a[j+1]*a[j]>0)
            {
                u+=a[j+1];
                j++;
            }
            i=j;
            sum[++cnt]=u;
        }
        set<pair<ll,int> > s;
        if(sum[1]*sum[cnt]>0)//首末连通块正负性相同则合并
         {
             sum[1]+=sum[cnt];
             cnt--;
         }
        for(int i=1;i<=cnt;i++)
           {
               //cout<<sum[i]<<endl;
                if(sum[i]>0)res++;
               s.insert({abs(sum[i]),i});
           }
           for(int i=2;i<=cnt;i++)
           {
               l[i]=i-1;
               r[i-1]=i;
           }
           p=cnt;
           r[cnt]=1;
           l[1]=cnt;
        for(int i=res;i>m;)
        {
            int id=s.begin()->second;
            s.erase(s.begin());
            ans-=abs(sum[id]);
            sum[id]+=sum[l[id]]+sum[r[id]];
            if(l[id])
            {
                s.erase({abs(sum[l[id]]),l[id]});
                int cur=l[id];
                int ll=l[cur],rr=r[cur];
                if(ll)
                {
                    r[ll]=rr;
                }
                if(rr)
                {
                    l[rr]=ll;
                }
            }
           if(r[id])
            {
                s.erase({abs(sum[r[id]]),r[id]});
                int cur=r[id];
                int ll=l[cur],rr=r[cur];
                if(ll)
                {
                    r[ll]=rr;
                }
                if(rr)
                {
                    l[rr]=ll;
                }
            }
            if(sum[id])
            s.insert({abs(sum[id]),id});
            i--;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    HDU2159 二维完全背包
    HDU1401 BFS
    HDU2842 矩阵乘法
    CF2.E
    CF2.D
    *HDU2254 矩阵乘法
    CF2.C
    *HDU1907 博弈
    博弈
    *HDU2147 博弈
  • 原文地址:https://www.cnblogs.com/flyljz/p/14076078.html
Copyright © 2011-2022 走看看