zoukankan      html  css  js  c++  java
  • bzoj4247挂饰——压缩的动态规划

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4247

    1.dp之前要先按挂钩个数从大到小排序,不然挂钩一度用成负的也可能是正确的,不仅脚标难存,而且不知道各种时刻负多少以内是合法的。

    2.但是最多有4000000个挂钩,省掉一维勉强开下数组也就罢了,会TLE!

      考虑多余n=2000个的挂钩就不会被用上了,所以第二维开成2000;

      (——状态表示至少有 j 个挂钩!!!)

      那么当a [ i ] > j 的时候怎么办呢?大家都是这么写的:

      d [ i ] [ j ] = max( d [ i -1 ] [ j ] , d [ i - 1 ] [ max( j - a [ i ] , 0) +1 ] + b [ i ] );

      也就是若a [ i ] > j,则d [ i ] [ j ] = max( d [ i -1 ] [ j ] , d [ i - 1 ] [ 1 ] + b [ i ] ),然后把多出来的挂钩忽略;

      为什么一定是1而不是k(1<=k<=j)呢?因为d [ ] [ 1 ]一定是最大的值!

      这是因为d [ ] [ 1 ]其实可能有很多挂钩,但把多余挂钩都忽略了,所以记录的其实是所有剩余挂钩中的最大值;

      那么0也是啊?

        但0的话不能再挂东西了,而只要剩下1个挂钩,就可以往上挂下一个东西,所以1可以一直被更新;

      如果剩下的东西都是0挂钩怎么办?

        所以才不能只记录d [ ] [ 1 ],而要记录到n,这样该挂饰实际有多个挂钩的优势也不会被忽略了!

    3.所以到最后1表示有剩余挂钩的最大值,0表示无剩余挂钩的最大值;

      但其实每次都会用1的值更新0的值,所以最后直接输出0的值就行了!也就是像状态定义的一样,0包含了所有情况。

    4.突然想到,那么即使当 j - a [ i ] > 0,也不一定用 d [ i -1 ] [ j - a [ i ] ]来更新,只需用 k ( j - a [ i ] <= k <= j )然后忽略多余挂钩就行了;

      那为什么不这样呢?因为 j 的值越大,相当于对挂钩数的限制越严,卡掉的状态也越多,值就越来越不优了!

      所以尽量用靠前的 j 值来更新!

    5.所以应该是n^2的复杂度才对,为什么是约6000ms?而且那种省掉一维的写法为什么不行?

    updt(2018.9.18):当j==0时max(j-r[i].a,0)+1==1,会用到本层的值,应该用上一层的值。

            折中的方法是滚动数组。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n;
    long long d[2005],ans;
    struct Node{
        int a,c;
    }r[2005];
    bool cmp(Node x,Node y){return x.a>y.a;}
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&r[i].a,&r[i].c);
        sort(r+1,r+n+1,cmp);
        memset(d,-2,sizeof d);
        d[1]=0;
        for(int i=1;i<=n;i++)
        {
    //        printf("i=%d a[i]=%d c[i]=%d
    ",i,r[i].a,r[i].c);
    //        lm+=r[i].a;
            if(!r[i].a)//
                for(int j=0;j<=n;j++)
                    d[j]=max(d[j],d[j+1]+r[i].c);
            else for(int j=n;j>=0;j--)
                d[j]=max(d[j],d[max(j-r[i].a,0)+1]+r[i].c);
        }
        ans=-2e9-7;
        for(int i=0;i<=n;i++)//i=0
            ans=max(ans,d[i]);
        printf("%lld",ans);
        return 0;
    }
    省掉一维的WA
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n;
    long long d[2005][2005],ans;
    struct Node{
        int a,c;
    }r[2005];
    bool cmp(Node x,Node y){return x.a>y.a;}
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&r[i].a,&r[i].c);
        sort(r+1,r+n+1,cmp);
        memset(d,-2,sizeof d);
        d[0][1]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=n;j++)
                d[i][j]=max(d[i-1][j],d[i-1][max(j-r[i].a,0)+1]+r[i].c);
        }
    //    ans=-2e9-7;
    //        for(int i=0;i<=n;i++)//i=0
    //            ans=max(ans,d[n][i]);
    //    ans=max(d[n][0],d[n][1]);
        printf("%lld",d[n][0]);
        return 0;
    }
  • 相关阅读:
    【LeetCode】204
    【LeetCode】231
    【LeetCode】58
    解决error104 socket error问题
    爬虫问题
    80端口被system占用的问题
    Linux命令行下批量重命名文件名为数字索引编号(0~N.xxx)的方法
    [转]利用excel进行线性规划求解
    python——时间与时间戳之间的转换
    最全中文停用词表整理(1893个)
  • 原文地址:https://www.cnblogs.com/Narh/p/8724197.html
Copyright © 2011-2022 走看看