~背包专题~01背包初体验

来源:互联网 发布:豆瓣评分算法 编辑:程序博客网 时间:2024/06/06 12:22

最近一直集训学习背包,刚开始就01背包,小小01背包里面有好多道道,今天就讲解01背包的模板题和求第K次最优解这样的两种题型,以后会不断的更新,毕竟有背包九解,需要学习的东西还有很多。

首先01背包题目的雏形是

N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。

从这个题目中可以看出,01背包的特点就是:每种物品仅有一件,可以选择放或不放。

其状态转移方程是:

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

对于这方方程其实并不难理解,方程之中,现在需要放置的是第i件物品,这件物品的体积是c[i],价值是w[i],因此f[i-1][v]代表的就是不将这件物品放入背包,而f[i-1][v-c[i]]+w[i]则是代表将第i件放入背包之后的总价值,比较两者的价值,得出最大的价值存入现在的背包之中。

HDU2546:饭卡
http://acm.hdu.edu.cn/showproblem.php?pid=2546

这是一个非常经典的DP题目,就是如果饭卡大于等于五块钱就可以透支,所以提前把总余额减去5块钱让他去买最贵的饭,对所有的饭排个序,然后DP下 看看怎么买 能花最多的钱,最后别忘了加上最贵的那就行,还有余额低于5直接输出就行,WA好几次就是忘了这个问题。标准的01背包问题 

#include<stdio.h>#include<iostream> #include <algorithm>#include<string.h>#include<math.h>#include<queue>#include<set>#define LL long long#define s(n) scanf("%d",&n)#define p(n) printf("%d",n)using namespace std;int cai[1005];int dp[1505][1505];bool cmp(int a, int b){    return a>b;}int main(){    int n,yu,maxx;    while(scanf("%d",&n)!=EOF)    {        if(n==0)            break;        for(int i=0;i<n;i++)            scanf("%d",&cai[i]);        scanf("%d",&yu);        maxx=0;        memset(dp,0,sizeof(dp));        sort(cai,cai+n,cmp);        if(yu>=5)        {            for(int i=1;i<n;i++)            {                for(int j=0;j<=yu-5;j++)                {                    if(j>=cai[i])                        dp[i][j]=max(dp[i-1][j],dp[i-1][j-cai[i]]+cai[i]);                    else                        dp[i][j]=dp[i-1][j];                    maxx=max(dp[i][j],maxx);                }            }            maxx=maxx+cai[0];            maxx=yu-maxx;            printf("%d\n",maxx);        }        else            printf("%d\n",yu);    }}

HDU2602:Bone Collectorhttp://acm.hdu.edu.cn/showproblem.php?pid=2602给你很多石头的体积和价值,让你放到一个口袋里面,看看如何使它的价值最大,标准的01背包问题#include<stdio.h>#include<iostream> #include <algorithm>#include<string.h>#include<math.h>#include<queue>#include<set>#define LL long long#define s(n) scanf("%d",&n)using namespace std;int m1[1100];int v1[1100];int dp[1100][1100];int main(){    int T,m,v;    while(scanf("%d",&T)!=EOF)    {        while(T--)        {            scanf("%d%d",&m,&v);            for(int i=1;i<=m;i++)                scanf("%d",&m1[i]);            for(int i=1;i<=m;i++)                scanf("%d",&v1[i]);            memset(dp,0,sizeof(dp));            int maxx=0;            for(int i=1;i<=m;i++)            {                for(int j=0;j<=v;j++)                {                    if(j>=v1[i])                    {                        dp[i][j]=max(dp[i-1][j],dp[i-1][j-v1[i]]+m1[i]);                    }                    else                         dp[i][j]=dp[i-1][j];                }            }            printf("%d\n",dp[m][v]);        }    }     return 0;}然后继续看到了一个求第K优解HDU2639:Bone Collector II(01背包第k优解)http://acm.hdu.edu.cn/showproblem.php?pid=2639这个题目很有意思,理解了好久,打印出来它储存和覆盖的过程,一步一步的把自己的想法改变,最后才懂了这样做的是为了啥,不得不佩服为啥会这样的神奇。还需要注意的是去重,代码上有相关的题型,对于第K优解相关的解释可以看        http://blog.sina.com.cn/s/blog_154d912700102wbvt.html#include<stdio.h>#include<iostream> #include <algorithm>#include<string.h>#include<math.h>#include<queue>#include<set>#define LL long long#define s(n) scanf("%d",&n)using namespace std;int m1[1100];int v1[1100];int dp[1100][40];int main(){int T,m,v,k,d;int a[40];int b[40];while(scanf("%d",&T)!=EOF){while(T--){scanf("%d%d%d",&m,&v,&k);for(int i=1;i<=m;i++)scanf("%d",&m1[i]);for(int i=1;i<=m;i++)scanf("%d",&v1[i]);memset(dp,0,sizeof(dp));for(int i=1;i<=m;i++){for(int j=v;j>=v1[i];j--){for(d=1;d<=k;d++){a[d]=dp[j-v1[i]][d]+m1[i]; //都放  刷新最优解 b[d]=dp[j][d];             //都不放 }int x,y,z;x=y=z=1;a[d]=b[d]=-1;while(z<=k && (x<=k || y<=k)) //此过程不但求出了前K次解 还刷新了DP {if(a[x]>b[y]){ dp[j][z]=a[x];x++;}else{dp[j][z]=b[y];y++; }  if(dp[j][z]!=dp[j][z-1])//去重复 z++; }}}printf("%d\n",dp[v][k]);} }return 0;}我大概说下我对于这个K优解的自己的感悟,可能不太全或者不太好,欢迎提出来,大家共同进步。这个必须从后往前走,也就是说从体积最大的开始,如果从最小的会出现叠加现象,所以从大往前。其实就相当于把那个体积下低于第i个物体的情况都弄出来,但是是去重之后的,只求出前K个情况下,然后第i+1个物体也从最大开始往前,这样就用到了第i个物体所保留的最优解,然后依次向前覆盖,最后留下的数组dp中就是最后中最优解,dp[v][0]----dp[v][k]代表的是从最大的那个解到第K个解所有的可能,去重之后的,如果输出的K大于它有的可能性,那就会输出0,所以初始化很重要。如果还是不太理解的话,你可以打印出来一些数据看一下~完全背包:完全背包是基于01背包的基础上延伸出来的,唯一的区别是01背包每件物品只有一件,然而完全背包是只要背包撑不破就可以一直装,下面带来几道完全背包的经典题目。① HDU 1114 点击打开链接Piggy-BankTime Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 27285    Accepted Submission(s): 13796Problem DescriptionBefore ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from Irreversibly Bound Money (IBM). The idea behind is simple. Whenever some ACM member has any small money, he takes all the coins and throws them into a piggy-bank. You know that this process is irreversible, the coins cannot be removed without breaking the pig. After a sufficiently long time, there should be enough cash in the piggy-bank to pay everything that needs to be paid. But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs!  InputThe input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing two integers E and F. They indicate the weight of an empty pig and of the pig filled with coins. Both weights are given in grams. No pig will weigh more than 10 kg, that means 1 <= E <= F <= 10000. On the second line of each test case, there is an integer number N (1 <= N <= 500) that gives the number of various coins used in the given currency. Following this are exactly N lines, each specifying one coin type. These lines contain two integers each, Pand W (1 <= P <= 50000, 1 <= W <=10000). P is the value of the coin in monetary units, W is it's weight in grams.  OutputPrint exactly one line of output for each test case. The line must contain the sentence "The minimum amount of money in the piggy-bank is X." where X is the minimum amount of money that can be achieved using coins with the given total weight. If the weight cannot be reached exactly, print a line "This is impossible.".  Sample Input310 11021 130 5010 11021 150 301 6210 320 4 Sample OutputThe minimum amount of money in the piggy-bank is 60.The minimum amount of money in the piggy-bank is 100.This is impossible.题目是小猪有一个存钱罐,给你罐子开始的重量和放满了钱的重量,让你判断下给的钱能不能使罐子放满,能输出最小放多少枚金币,不能输出This is impossible.很简单的完全背包,上代码,代码都有注解;#include<stdio.h>#include<iostream> #include <algorithm>#include<string.h>#include<math.h>#include<queue>#include<set>#define LL long long#define INF 0x3f3f3f3fusing namespace std; struct po{int P;int W;}p[600];int dp[100005];//记录重量的DP int main(){int T,n,m,t;while(scanf("%d",&T)!=EOF){while(T--){scanf("%d%d",&n,&m);scanf("%d",&t);for(int i=0;i<t;i++)scanf("%d%d",&p[i].P,&p[i].W);//金币数目 重量 int temp=m-n;//应该放的重量if(temp<=0)//放0个 cout<<"The minimum amount of money in the piggy-bank is 0.\n"<<endl;else{for(int j=1;j<=temp;j++)//初始化最大  容易和后边小的比较dp[j]=INF;for(int i=0;i<t;i++){ for(int j=p[i].W;j<=temp;j++)//从当前可以放进去的最小重量开始 { dp[j]=min(dp[j],dp[j-p[i].W]+p[i].P);//更新数据 }} if(dp[temp]==INF)//不能放满 printf("This is impossible.\n");elseprintf("The minimum amount of money in the piggy-bank is %d.\n",dp[temp]);}}}return 0;} HDU 1248 点击打开链接中文题意 自己看下 完全背包 求最小的小费  完全背包的水题此题暴力也可以解决寒冰王座Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 18186    Accepted Submission(s): 9470Problem Description不死族的巫妖王发工资拉,死亡骑士拿到一张N元的钞票(记住,只有一张钞票),为了防止自己在战斗中频繁的死掉,他决定给自己买一些道具,于是他来到了地精商店前.死亡骑士:"我要买道具!"地精商人:"我们这里有三种道具,血瓶150块一个,魔法药200块一个,无敌药水350块一个."死亡骑士:"好的,给我一个血瓶."说完他掏出那张N元的大钞递给地精商人.地精商人:"我忘了提醒你了,我们这里没有找客人钱的习惯的,多的钱我们都当小费收了的,嘿嘿."死亡骑士:"......"死亡骑士想,与其把钱当小费送个他还不如自己多买一点道具,反正以后都要买的,早点买了放在家里也好,但是要尽量少让他赚小费.现在死亡骑士希望你能帮他计算一下,最少他要给地精商人多少小费. Input输入数据的第一行是一个整数T(1<=T<=100),代表测试数据的数量.然后是T行测试数据,每个测试数据只包含一个正整数N(1<=N<=10000),N代表死亡骑士手中钞票的面值.注意:地精商店只有题中描述的三种道具. Output对于每组测试数据,请你输出死亡骑士最少要浪费多少钱给地精商人作为小费. Sample Input2900250 Sample Output050#include<stdio.h>#include<iostream> #include <algorithm>#include<string.h>#include<math.h>#include<queue>#include<set>#define LL long long#define INF 0x3f3f3f3fusing namespace std; int dp[100005];int weight[3]={150,200,350};void biao()//打个表 {for(int i=0;i<3;i++){for(int j=1;j<=10000;j++){if(j>=wight[i])dp[j]=max(dp[j],weight[i]+dp[j-weight[i]]);}}} int main(){int n,T;biao();int ans=0;while(scanf("%d",&T)!=EOF){while(T--){scanf("%d",&n);printf("%d\n",n-dp[n]);}}return 0;} 


原创粉丝点击