• 动态规划


    1.01背包

    hdu 2602 Bone Collector(01背包)

    题意:给出包裹的大小v,然后给出n块骨头的价值value和体积volume,求出一路下来包裹可以携带骨头最大价值

    思路:01背包

    1.二维数组(不常用

    /*
    01背包
    二维数组
    */
    #include<iostream>
    #include<stdio.h>
    #include<math.h>
    using namespace std;
    int dp[1100][1100];
    int main()
    {
        int i,j;
        int t,n,_v;//测试用例个数,物品种类,背包大小
        int c[1100],v[1100];//花费,价值
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&_v);
            for(j=0; j<=_v; ++j)dp[0][j]=0;
    
            for(i=1; i<=n; ++i)scanf("%d",&v[i]);
            for(i=1; i<=n; ++i)scanf("%d",&c[i]);
    
            for(i=1; i<=n; ++i)
            {
                for(j=0; j<c[i]; ++j)dp[i][j]=dp[i-1][j];
                for(j=c[i]; j<=_v; ++j)
                {
                    if(dp[i-1][j-c[i]]+v[i]>dp[i-1][j])dp[i][j]=dp[i-1][j-c[i]]+v[i];
                    else dp[i][j]=dp[i-1][j];
                }
            }
    
            printf("%d
    ",dp[n][_v]);
        }
        return 0;
    }
    View Code

    2.一维数组(之后 多重、完全背包 的解法 皆采用一维数组)

    1...n
       _v...0

    /*
    01背包
    一维数组
    */
    #include<iostream>
    #include<stdio.h>
    using namespace std;
    int dp[1100];
    int main()
    {
        int i,j;
        int t,n,_v;//测试用例个数,物品种类,背包大小
        int c[1100],v[1100];//花费,价值
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&_v);
            for(j=0; j<=_v; ++j)dp[j]=0;
    
            for(i=1; i<=n; ++i)scanf("%d",&v[i]);
            for(i=1; i<=n; ++i)scanf("%d",&c[i]);
    
            for(i=1; i<=n; ++i)
                for(j=_v; j>=c[i]; --j)
                    if(dp[j-c[i]]+v[i]>dp[j])dp[j]=dp[j-c[i]]+v[i];
    
            printf("%d
    ",dp[_v]);
        }
        return 0;
    }
    View Code

    2.多重背包

    hdu 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)

    题意:给出经费的最大值n,再给出   种类m和每种的袋数c、价格p、重量h,求能买大米的最大重量

    思路:每种物品有一个固定的次数上限。为多重背包问题。转换为01背包来做

    以下方法,均为转化为01背包来做

    思路1:物品不摊开,选取每一种时,进行讨论,(相当于竖着填背包v的一列,一列一列的填

    码1:kj

    /*
    多重背包
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    int dp[110];
    int main()
    {
        int i,j,k,tem;
        int t,n,_v;//测试用例个数,物品种类,背包大小
        int c[110],v[110];//花费,价值
        int num[110];//每种物品个数
        int tc,tv;//拆分时物品花费,价值
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&_v,&n);
            memset(dp,0,sizeof(dp));
    
            for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]);
            //////
            for(i=1; i<=n; ++i)
                for(k=_v; k>=c[i]; --k)
                    for(j=1; j<=num[i]&&j*c[i]<=k; ++j)//此处比01背包多了一层循环
                    {
                        tc=j*c[i];
                        tv=j*v[i];
                        tem=dp[k-tc]+tv;
                        if(tem>dp[k])dp[k]=tem;
                    }
            //
            printf("%d
    ",dp[_v]);
        }
        return 0;
    }
    View Code

    思路2:物品摊开,然后处理01背包(相当于横着填物品的一行,一行一行的填

    2.1朴素拆分,把每种物品展开,既有n件a物品,则进行n件是否选取操作

    码2:jk

    /*
    多重背包
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    int dp[110];
    int main()
    {
        int i,j,k,tem;
        int t,n,_v;//测试用例个数,物品种类,背包大小
        int c[110],v[110];//花费,价值
        int num[110];//每种物品个数
        int tc,tv;//拆分时物品花费,价值
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&_v,&n);
            memset(dp,0,sizeof(dp));
    
            for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]);
            //////
            for(i=1; i<=n; ++i)
                for(j=1; j<=num[i]&&j*c[i]<=_v; ++j)//此处比01背包多了一层循环
                    for(k=_v; k>=j*c[i]; --k)
                    {
                        tem=dp[k-c[i]]+v[i];
                        if(tem>dp[k])dp[k]=tem;
                    }
            //
            printf("%d
    ",dp[_v]);
        }
        return 0;
    }
    View Code

    hint:2.1代码与思路1 代码 只是循环次序不一样而已,实际是一样的操作。(并且时间复杂度相同,略麻烦。
    因为 
    思路1:
    for(k=_v; k>=c[i]; --k)
          for(j=1; j<=num[i]&&j*c[i]<=k; ++j)
    思路2:
    for(j=1; j<=num[i]&&j*c[i]<=_v; ++j)
          for(k=_v; k>=j*c[i]; --k)

    大体看一下这俩循环,

    这两个循环做的循环次数是相同的,也就是说,计算次数相同,时间复杂度相同

    思路2.2  二进制拆分,有n件a物品,则拆成 1,2,4,8,...,q 这样,即 1+2+4+...+q=n

    码3:

    /*
    多重背包
    二进制拆分
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    int dp[110];
    int main()
    {
        int i,j,k,tem;
        int t,n,_v;//测试用例个数,物品种类,背包大小
        int c[110],v[110];//花费,价值
        int num[110];//每种物品个数
        int tc,tv;//拆分时物品花费,价值
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&_v,&n);
            memset(dp,0,sizeof(dp));
    
            for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]);
            //////
            for(i=1; i<=n; ++i)
            {
                for(j=1; j<=num[i]; num[i]=num[i]-j,j=j*2)//此处比01背包多了一层循环
                {
                    tc=j*c[i];//拆分后物品花费
                    tv=j*v[i];//
                    for(k=_v; k>=tc; --k)
                    {
                        tem=dp[k-tc]+tv;
                        if(tem>dp[k])dp[k]=tem;
                    }
                }
                if(num[i]>0) //如果还有物品,num[i] 即相当于 1+2+4+...+q  中的 q
                {
                    tc=num[i]*c[i];
                    tv=num[i]*v[i];
                    for(k=_v; k>=tc; --k)
                    {
                        tem=dp[k-tc]+tv;
                        if(tem>dp[k])dp[k]=tem;
                    }
                }
            }
        //
            printf("%d
    ",dp[_v]);
        }
        return 0;
    }
    View Code

    3.完全背包

    hdu 1114 Piggy-Bank(完全背包)

    题意:给出一个存钱罐的容量,给出n种硬币的价值p和重量w(注意:每种硬币可无限取)

    1.如果存钱罐能够正好塞满,输出塞满存钱罐需要的最少硬币的价值。

    2.若不能完全塞满,则输出impossible。

    思路:每种物品可以放无限多次。所以为完全背包问题。此题是求最小值,为完全背包的变形。

    注意初始化 dp[ 0 ]=0;

    for i=1..N

        for v=0..V

            f[v]=max{f[v],f[v-cost]+weight}

     

    /*
    完全背包
    */
    #include<iostream>
    #include<stdio.h>
    using namespace std;
    
    int main(){
        int t,e,f;//测试用例,空存钱罐的重量,装满后存钱罐的重量
        int n,p,w;//硬币种类,价值,重量
        int dp[11000];//最小价值
        dp[0]=0;//初始化为0
        scanf("%d",&t);
        while(t--){
            scanf("%d%d",&e,&f);
            int v=f-e;//存钱罐的容量
            int i,j;
            for(i=1;i<=v;i++)
                dp[i]=1234567890;
            scanf("%d",&n);
            for(i=0;i<n;i++){
                scanf("%d%d",&p,&w);
                for(j=w;j<=v;j++){
                    int tem=dp[j-w]+p;
                    if(tem<dp[j])
                        dp[j]=tem;
                }
            }
            if(dp[v]==1234567890)
                printf("This is impossible.
    ");
            else
                printf("The minimum amount of money in the piggy-bank is %d.
    ",dp[v]);
        }
        return 0;
    }
    View Code

    4.最长上升子序列O(nlogn)

    /*
    最长上升子序列O(nlogn)
    */
    #include<iostream>
    #include<stdio.h>
    using namespace std;
    
    const int MAXN=500010;
    int a[MAXN],b[MAXN];
    //b[k]是序列a中所有长度为k的递增子序列中的最小结尾元素值
    //用二分查找的方法找到一个位置,使得num>b[i-1]并且num<b[i],并用num代替b[i]
    int Search(int num,int low,int high){
        int mid;
        while(low<=high){
            mid=(low+high)/2;
            if(num>=b[mid])low=mid+1;
            else high=mid-1;
        }
        return low;
    }
    int DP(int n){
        int i,len,pos;
        b[1]=a[1];
        len=1;
        for(i=2;i<=n;i++){
            if(a[i]>=b[len]){//如果a[i]比b[]数组中最大还大直接插入到后面即可
                len=len+1;
                b[len]=a[i];
            }
            else{//用二分的方法在b[]数组中找出第一个比a[i]大的位置并且让a[i]替代这个位置
                pos=Search(a[i],1,len);
                b[pos]=a[i];
            }
        }
        return len;
    }
    
    int main(){
    
        a[1]=1;
        a[2]=2;
        a[3]=3;
        a[4]=0;
    
        printf("%d
    ",DP(4));
    
        return 0;
    }
    View Code
  • 相关阅读:
    最全!即学即会 Serverless Devs 基础入门(上)
    一站式智能运维解决方案,企业系统的隐形守护者
    Serverless JOB | 传统任务新变革
    好的 MySQL 兼容性可以做到什么程度? PolarDBX 如何做生态兼容
    Apsara Stack 技术百科 | 如何「场景化」的企业上云
    事务、全局索引、透明分布式,再见,分区健!
    IT人才能嗑到的这对CP,甜!
    腾讯云:最简单CentOS7下安装GUI图形界面(最新)
    Navicat连接不上Mysql 8问题解决方案
    【JS】验证表单是否空白验证
  • 原文地址:https://www.cnblogs.com/gongpixin/p/5366580.html
Copyright © 2020-2023  润新知