zoukankan      html  css  js  c++  java
  • 水dp第二天(背包有关)

    水dp第二天(背包有关)

    标签: dp


    poj_3624

    1. 题意:裸的01背包
    2. 注意:这种题要注意两个问题,一个是要看清楚数组要开的范围大小,然后考虑需要空间优化吗,还有事用int还是long long
    3. 代码:
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 13000;
    int W[N],D[N];
    int dp[N];
    int main()
    {
        int n,m;
        while(~scanf("%d%d",&n,&m))
        {
            memset(dp,0,sizeof(dp));
            for(int i = 0; i < n; i++){
                scanf("%d%d",&D[i],&W[i]);
            }
            for(int i = 0; i < n; i++){
                for(int j = m; j >= D[i]; j--){
                    dp[j] = max(dp[j],dp[j-D[i]]+W[i]);
                }
            }
            printf("%d
    ",dp[m]);
        }
        return 0;
    }
    
    

    poj_2533

    1. 题意:最长上升子序列
    2. 注意:如果不要求输出上升序列的话就可以采用O(nlogn)的算法
    3. 算法讲解:维护一个数组,每次考虑一个新加入的元素时,如果这个元素比数组最后一个大的话,那么直接把这个元素放在数组的后面,否则的话,通过二分查找找到这个数组中第一个比当前值大的元素,替换这个元素,因为新加入的元素比这个元素更具有“潜力”。
    4. 算法注意:二分的时候一定要仔细是L<=R因为这个wa了好多次。还要注意因为是每次都要判断是否可以吧这个元素加入数组,所以考虑0的情况则不能把数组初始化为0,而应初始化为-1,并且数组应该是从1开始标号,保证第一个数可以通过判断加入数组中。(考虑一下,通过这样方式得到的数组一定是一个有序的数组,而且一定是一个增序的数组),如果要想得到不减的数组,在和最后一个元素比较的时候相等也加入队列即可。
    5. 代码:
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 1010;
    int stk[N];
    int cnt;
    int BSearch(int l,int r, int c)
    {
        while(l<=r){//注意这里应该是定于的时候还要再判断一次。
            int m = (l+r)>>1;
            if(stk[m]==c) {
                //printf("id = %d
    ",m);
                return m;
            }
            else if(stk[m]<c) l = m+1;
            else if(stk[m]>c) r = m-1;
        }
        //printf("id = %d
    ",l);
        return l;
    }
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            if(n==0) {puts("1"); continue;}
            int cnt = 1;//因为cnt = 0的时候第一个数字没办法判断入栈情况
            memset(stk,-1,sizeof(stk));
            int tm;
            for(int i = 0; i < n; i++){
                scanf("%d",&tm);
                if(tm>stk[cnt-1]){
                    stk[cnt++] = tm;
                }
                else {
                    int id = BSearch(1,cnt-1,tm);
                    stk[id] = tm;
                }
            }
            printf("%d
    ",cnt-1);
        }
        return 0;
    }
    
    

    hdu_2546

    1. 题意:中文题意说的很清楚了
    2. 题解:这个题有意思,因为是如果最后还剩下5块钱的时候就可以买任意多的东西,所以考虑用最后的5块钱去买最贵的一种,然后剩下的n-1中物品就要尽可能多的花到总钱数-5,就是一个典型的01背包问题了。那么问题自然来了,考虑两种特殊情况,如果一开始钱就小于5,或者是给的钱-5就可以吧所有的东西都买完了,这两种情况很简单,特判一下就可以了
    3. 代码:
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 2000;
    int cost[N];
    int dp[N];//dp[i][j]表示考虑到第i个菜余额是j的时候的最大花费
    int main()
    {
        int n,m,sum;
        while(~scanf("%d",&n),n)
        {
            sum = 0;
            for(int i = 0; i < n; i++){
                scanf("%d",&cost[i]);
                sum+=cost[i];
            }
            sort(cost, cost+n);
            scanf("%d",&m);
            if(m<5){
                printf("%d
    ",m);
                continue;
            }
            if(m>sum){
                printf("%d
    ",m-sum);
                continue;
            }
            m = m-5;
            memset(dp,0,sizeof(dp));
            for(int i = 0; i < n-1; i++){
                for(int j = m; j>=cost[i]; j--){
                    dp[j] = max(dp[j],dp[j-cost[i]]+cost[i]);
                }
            }
            printf("%d
    ",m+5-dp[m]-cost[n-1]);
        }
        return 0;
    }
    
    

    hdu_1114

    1. 题意:要猜一个存钱罐里最少有多少钱。而且如果不存在一种可能装到当前重量的话,要输出不可能
    2. 题解:这是很简单的完全背包恰好被装满的情况,这种问题和一般的背包比较就是初始化不同,要把所有不符合条件的状态标记成-1,然后符合条件的状态可以进行转移。
    3. 注意:要精确的吧dp写对,就一定要把每个状态是从哪个状态转移来的,每个状态一旦确定会不会受到后面状态的影响想清楚,仔细画一个图也可以很清楚的找到这个空间优化的方法了
    4. 代码:
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 550;
    int p[N],w[N];
    int dp[N*500];
    int main()
    {
        int T,n,l,r,val;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&l,&r);
            val = r-l;
            scanf("%d",&n);
            for(int i = 0; i < n; i++){
                scanf("%d%d",&p[i],&w[i]);
            }
            memset(dp,-1,sizeof(dp));
            dp[0] = 0;
            for(int i = 0; i < n; i++){
                for(int j = w[i]; j <= val; j++){
                    if(dp[j]==-1&&dp[j-w[i]]==-1) continue;
                    else if(dp[j]==-1) dp[j] = dp[j-w[i]]+p[i];
                    else if(dp[j-w[i]] == -1) dp[j] = dp[j];
                    else dp[j] = min(dp[j],dp[j-w[i]]+p[i]);
                }
            }
            if(dp[val]==-1) puts("This is impossible.");
            else printf("The minimum amount of money in the piggy-bank is %d.
    ",dp[val]);
        }
        return 0;
    }
    
    

    hdu_2191

    1. 题意:这个题是一个裸的多重背包。可以直接套用模板,经过我被上一个模板坑了1个小时的精力,终于把之前的模板调通了
    2. 代码:
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    #define N 110
    
    int val;
    int f[N*500];
    
    //每件物品只能使用一次
    void onezeropack(int v,int c)
    {
        int j;
        for(j=val; j>=v; j--)
        {
            f[j]=max(f[j-v]+c,f[j]);
        }
    }
    //每件物品可以无限使用
    void completepack(int v,int c)
    {
        int j;
        for(j=v; j<=val; j++)
        {
            f[j]=max(f[j-v]+c,f[j]);
        }
    }
    //每件物品有限次使用
    void multiplepack(int v,int c,int num)
    {
        if(v*num>=val)
        {
            completepack(v,c);
            return;
        }
        int k=1;
        while(k<num)
        {
            onezeropack(k*v,k*c);
            num=num-k;
          //  printf(" - num = %d
    ", num);
            k=k*2;
        }
        //printf("num = %d
    ", num);
        onezeropack(num*v,num*c);
    }
    
    
    int v[N], num[N], w[N];
    int main()
    {
        int T,n,m;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            val = n;
            for(int i = 0; i < m; i++){
                scanf("%d%d%d",&v[i],&w[i],&num[i]);
            }
            memset(f,0,sizeof(f));
            for(int i = 0; i < m; i++){
                multiplepack(v[i],w[i],num[i]);
            }
            printf("%d
    ",f[val]);
        }
        return 0;
    }
    

    hdu_2844

    1. 又是一道背包水题
    2. 代码:
    #include <stdio.h>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    
    const int MAX=100000;
    int dp[MAX];
    int c[MAX],w[MAX];
    int v;
    
    void ZeroOnePack(int cost,int wei)//01
    {
        int i;
        for(i = v;i>=cost;i--)
        {
            dp[i] = max(dp[i],dp[i-cost]+wei);
        }
    }
    
    void CompletePack(int cost,int wei)//完全
    {
        int i;
        for(i = cost;i<=v;i++)
        {
            dp[i] = max(dp[i],dp[i-cost]+wei);
        }
    }
    
    void MultiplePack(int cost,int wei,int cnt)//多重
    {
        if(v<=cnt*cost)//如果总容量比这个物品的容量要小,那么这个物品可以直到取完,相当于完全背包
        {
            CompletePack(cost,wei);
            return ;
        }
        else//否则就将多重背包转化为01背包
        {
            int k = 1;
            while(k<=cnt)
            {
                ZeroOnePack(k*cost,k*wei);
                cnt = cnt-k;
                k = 2*k;
            }
            ZeroOnePack(cnt*cost,cnt*wei);
        }
    }
    
    int main()
    {
        int n;
        while(~scanf("%d%d",&n,&v),n+v)
        {
            int i;
            for(i = 0;i<n;i++)
            scanf("%d",&c[i]);
            for(i = 0;i<n;i++)
            scanf("%d",&w[i]);
            memset(dp,0,sizeof(dp));
            for(i = 0;i<n;i++)
            {
                MultiplePack(c[i],c[i],w[i]);
            }
            int sum = 0;
            for(i = 1;i<=v;i++)
            {
                if(dp[i]==i)
                {
                    sum++;
                }
            }
            printf("%d
    ",sum);
        }
        return 0;
    }
    

    hdu_1171

    1. 做过。。。看之前的博客吧
    2. 代码,这次是直接手写的多重背包转化成01背包,不用模板的
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int v[5005];
    int dp[255555];
    int main()
    {
        int n;
        int cnt;
        while(~scanf("%d",&n))
        {
            if(n<=0) return 0;
            int val,c;
            cnt = 0;
            int sum = 0;
            for(int i = 0; i < n; i++){
                scanf("%d%d",&val,&c);
                sum+=val*c;
                while(c--){
                    v[cnt++] = val;
                }
            }
            sum = sum;
            memset(dp,0,sizeof(dp));
            for(int i = 0; i < cnt; i++){
                for(int j = sum/2; j >= v[i]; j--){
                    dp[j] = max(dp[j],dp[j-v[i]]+v[i]);
                }
            }
            printf("%d %d
    ",sum-dp[sum/2],dp[sum/2]);
        }
        return 0;
    }
    
    
  • 相关阅读:
    boost::ASIO的异步方式
    C++ 类构造函数初始化列表
    C++11 shared_ptr(智能指针)详解
    C/C++中静态成员变量和静态成员函数的含义与不同用法
    静态局部变量
    GDB入门教程之如何使用GDB启动调试
    Qt的QWaitCondition了解一下吧
    Qt信号量QSemaphore认识一下
    瞧一瞧Qt的QMutex
    Qt的读写锁QReadWriteLock要怎么玩?
  • 原文地址:https://www.cnblogs.com/shanyr/p/5722557.html
Copyright © 2011-2022 走看看