轉自http://blog.csdn.net/lyhvoyage/article/details/8545852ios
01 揹包算法
有n 種不一樣的物品,每一個物品有兩個屬性,size 體積,value 價值,如今給一個容量爲 w 的揹包,問最多可帶走多少價值的物品。 ide
- int f[w+1];
- for (int i=0; i<n; i++)
- for (int j=w; j>=size[i]; j--)
- f[j] = max(f[j], f[j-size[i]]+value[i]);
徹底揹包 優化
若是物品不計件數,就是每一個物品不僅一件的話,稍微改下便可 spa
- for (int i=0; i<n; i++)
- for (int j=size[i]; j<=w; j++)
- f[j] = max(f[j], f[j-size[i]]+value[i]);
f[w] 即爲所求
初始化分兩種狀況:
一、若是揹包要求正好裝滿則初始化 f[0] = 0, f[1~w] = -INF;
二、若是不須要正好裝滿 f[0~v] = 0; .net
舉例:code
01揹包orm
V=10,N=3,c[]={3,4,5}, w={4,5,6}blog
(1)揹包不必定裝滿ip
計算順序是:從右往左,自上而下:由於每一個物品只能放一次,前面的體積小的會影響體積大的
(2)揹包恰好裝滿
計算順序是:從右往左,自上而下。注意初始值,其中-inf表示負無窮
徹底揹包
:
V=10,N=3,c[]={3,4,5}, w={4,5,6}
(1)揹包不必定裝滿
計算順序是:從左往右,自上而下: 每一個物品能夠放屢次,前面的會影響後面的
(2)揹包恰好裝滿
計算順序是:從左往右,自上而下。注意初始值,其中-inf表示負無窮
多重揹包
:
多重揹包問題要求很簡單,就是每件物品給出肯定的件數,求
可獲得的最大價值
多重揹包轉換成 01 揹包問題就是多了個初始化,把它的件數C 用二進制
分解成若干個件數的集合,這裏面數字能夠組合成任意小於等於C
的件數,並且不會重複,之因此叫二進制分解,是由於這樣分解可
以用數字的二進制形式來解釋
好比:7的二進制 7 = 111 它能夠分解成 001 010 100 這三個數能夠
組合成任意小於等於7 的數,並且每種組合都會獲得不一樣的數
15 = 1111 可分解成 0001 0010 0100 1000 四個數字
若是13 = 1101 則分解爲 0001 0010 0100 0110 前三個數字能夠組合成
7之內任意一個數,即一、二、4能夠組合爲1——7內全部的數,加上 0110 = 6 能夠組合成任意一個大於6 小於等於13
的數,好比12,可讓前面貢獻6且後面也貢獻6就好了。雖然有重複但老是能把 13 之內全部的數都考慮到了,基於這種
思想去把多件物品轉換爲,多種一件物品,就可用01 揹包求解了。
看代碼:
- int n;
- int c;
- int v;
- int s;
- int count = 0;
- int value[MAX];
- int size[MAX];
-
- scanf("%d", &n);
-
- while (n--)
- {
- scanf("%d%d%d", &c, &s, &v);
- for (int k=1; k<=c; k<<=1)
- {
- value[count] = k*v;
- size[count++] = k*s;
- c -= k;
- }
- if (c > 0)
- {
- value[count] = c*v;
- size[count++] = c*s;
- }
- }
定理:一個正整數n能夠被分解成1,2,4,…,2^(k-1),n-2^k+1(k是知足n-2^k+1>0的最大整數)的形式,且1~n以內的全部整數都可以惟一表示成1,2,4,…,2^(k-1),n-2^k+1中某幾個數的和的形式。
證實以下:
(1) 數列1,2,4,…,2^(k-1),n-2^k+1中全部元素的和爲n,因此若干元素的和的範圍爲:[1, n];
(2)若是正整數t<= 2^k – 1,則t必定能用1,2,4,…,2^(k-1)中某幾個數的和表示,這個很容易證實:咱們把t的二進制表示寫出來,很明顯,t能夠表示成n=a0*2^0+a1*2^1+…+ak*2^(k-1),其中ak=0或者1,表示t的第ak位二進制數爲0或者1.
(3)若是t>=2^k,設s=n-2^k+1,則t-s<=2^k-1,於是t-s能夠表示成1,2,4,…,2^(k-1)中某幾個數的和的形式,進而t能夠表示成1,2,4,…,2^(k-1),s中某幾個數的和(加數中必定含有s)的形式。
(證畢!)
如今用count 代替 n 就和01 揹包問題徹底同樣了
杭電2191題解:
此爲多重揹包用01和徹底揹包:
- #include<stdio.h>
- #include<string.h>
- int dp[102];
- int p[102],h[102],c[102];
- int n,m;
- void comback(int v,int w)
- {
- for(int i=v; i<=n; i++)
- if(dp[i]<dp[i-v]+w)
- dp[i]=dp[i-v]+w;
- }
- void oneback(int v,int w)
- {
- for(int i=n; i>=v; i--)
- if(dp[i]<dp[i-v]+w)
- dp[i]=dp[i-v]+w;
- }
- int main()
- {
- int ncase,i,j,k;
- scanf("%d",&ncase);
- while(ncase--)
- {
- memset(dp,0,sizeof(dp));
- scanf("%d%d",&n,&m);
- for(i=1; i<=m; i++)
- {
- scanf("%d%d%d",&p[i],&h[i],&c[i]);
- if(p[i]*c[i]>=n) comback(p[i],h[i]);
- else
- {
- for(j=1; j<c[i]; j<<1)
- {
- oneback(j*p[i],j*h[i]);
- c[i]=c[i]-j;
- }
- oneback(p[i]*c[i],h[i]*c[i]);
- }
- }
- printf("%d\n",dp[n]);
- }
- return 0;
- }
只是用01揹包,用二進制優化:
- #include <iostream>
- using namespace std;
- int main()
- {
- int nCase,Limit,nKind,i,j,k, v[111],w[111],c[111],dp[111];
-
-
- int count,Value[1111],size[1111];
-
-
-
- cin>>nCase;
- while(nCase--)
- {
- count=0;
- cin>>Limit>>nKind;
- for(i=0; i<nKind; i++)
- {
- cin>>w[i]>>v[i]>>c[i];
-
- for(j=1; j<=c[i]; j<<=1)
- {
-
- Value[count]=j*v[i];
- size[count++]=j*w[i];
- c[i]-=j;
- }
- if(c[i]>0)
- {
- Value[count]=c[i]*v[i];
- size[count++]=c[i]*w[i];
- }
- }
-
-
-
-
-
- memset(dp,0,sizeof(dp));
- for(i=0; i<count; i++)
- for(j=Limit; j>=size[i]; j--)
- if(dp[j]<dp[j-size[i]]+Value[i])
- dp[j]=dp[j-size[i]]+Value[i];
-
- cout<<dp[Limit]<<endl;
- }
- return 0;
- }
未優化的:
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- using namespace std;
-
- int Value[105];
- int Cost[105];
- int Bag[105];
- int dp[105];
-
- int main()
- {
- int C,m,n;
- scanf("%d",&C);
- while(C--)
- {
- scanf("%d%d",&n,&m);
- for(int i = 1; i <= m; i++)
- scanf("%d%d%d",&Cost[i],&Value[i],&Bag[i]);
- memset(dp,0,sizeof(dp));
- for(int i=1; i<= m; i++)
- for(int j=1; j<=Bag[i]; j++)
- for(int k=n; k>=Cost[i]; k--)
- dp[k]=max(dp[k], dp[k-Cost[i]]+Value[i]);
- printf("%d\n",dp[n]);
- }
- return 0;
- }