1. 01揹包(HD 2602)
問題:有N件物品和一個容量爲V的揹包。第i件物品的體積是volume[i],價值是value[i]。求解將哪些物品裝入揹包可使價值總和最大。
(1)揹包一定裝滿
int ZeroOnePack(vector<int> value, vector<int> volume, int V)
{
int i, j;
vector<int> f;
for(i = 0; i <= V; i++)
f.push_back(0);
for(i = 0; i < value.size(); i++)
{
for(j = V; j >= volume[i]; j--)
{
f[j] = max(f[j], f[j-volume[i]]+value[i]);
}
}
return f[V];
}
(2)揹包不一定裝滿
</pre><pre name="code" class="cpp">int ZeroOnePack(vector<int> value, vector<int> volume, int V)
{
int i, j;
vector<int> f;
f.push_back(0);
for(i = 1; i <= V; i++) //區別就在於初始化
f.push_back(INF);
for(i = 0; i < value.size(); i++)
{
for(j = V; j >= volume[i]; j--)
{
f[j] = max(f[j], f[j-volume[i]]+value[i]);
}
}
return f[V];
}
爲什麼呢?可以這樣理解:初始化的f數組事實上就是在沒有任何物品可以放入揹包時的合法狀態。如果要求揹包恰好裝滿,那麼此時只有容量爲0的揹包可能被價值爲0的nothing “恰好裝滿”,其它容量的揹包均沒有合法的解,屬於未定義的狀態,它們的值就都應該是 1了。如果揹包並非必須被裝滿,那麼任何容量的揹包都有一個合法解“什麼都不裝”,這個解的價值爲0,所以初始時狀態的值也就全部爲0了。這個小技巧完全可以推廣到其它類型的揹包問題。
2.完全揹包(HD 1114)
問題:有N種物品和一個容量爲V的揹包,每種物品都有無限件可用。第i種物品的體積是volume[i],價值是value[i]。求解將哪些物品裝入揹包可使這些物品的體積總和不超過揹包容量,且價值總和最大。
int CompletePack(vector<int> value, vector<int> volume, int V)
{
int i, j;
vector<int> f;
f.push_back(0);
for(i = 1; i <= V; i++)
f.push_back(INF);
for(i = 0; i < value.size(); i++)
{
for(j = volume[i]; j <= V; j++) //循環順序變了
{
f[j] = min(f[j], f[j-volume[i]]+value[i]);
}
}
return f[V];
}
區別於01揹包的就是for(j = volume[i]; j <= V; j++) //循環順序變了,在01揹包中的循環順序是爲了保證每件物品只選一次,保證在考慮“選入第i件物品”這件策略時,依據的是一個絕無已經選入第i件物品的子結果f[i-1][v-c[i]]。而現在完全揹包的特點恰是每種物品可選無限件,所以在考慮“加選一件第i種物品”這種策略時,卻正需要一個可能已選入第i種物品的子結果f[i][v-c[i]],所以就可以並且必須採用v=0..V的順序循環。
3.多重揹包(HD 1059)
問題:有N種物品和一個容量爲V的揹包。第i種物品最多有n[i]件可用,每件體積是volume[i],價值是value[i]。求解將哪些物品裝入揹包可使這些物品的體積總和不超過揹包容量,且價值總和最大。
思路:將第i種物品分成若干件物品,其中每件物品有一個係數,這件物品的體積和價值均是原來的體積和價值乘以這個係數。這些係數有個要求:保證對於0..n[i]間的每一個整數,均可以用若干個係數的和表示。這是爲了簡化分成若干件(0...n[i])。(二進制分解)這些係數爲:2^0,2^1,...,2^k,n[i] - 2^k+1,有定理保證,其實這個定理就是騰訊2015實習招聘時候附加題(http://blog.csdn.net/dongyi91/article/details/23548943)。
int MultiplePack(vector<int> value, vector<int> volume, int V, vector<int> count)
{
int i, j, k;
vector<int> f;
for(i = 0; i <= V; i++)
f.push_back(0);
for(i = 0; i < value.size(); i++)
{
if(volume[i]*count[i] >= V) //對待該物件相當於完全揹包問題
{
for(j = volume[i]; j <= V; j++) //完全揹包
{
f[j] = max(f[j], f[j-volume[i]]+value[i]);
}
}
else
{
k = 1;
while(k < count[i]) //分解若干個物件用01揹包問題解
{
for(j = V; j >= k*volume[i]; j--) //01揹包
{
f[j] = max(f[j], f[j-k*volume[i]] + k*value[i]);
}
count[i] -= k;
k *= 2; //二進制分解
}
for(j = V; j >= count[i]*volume[i]; j--) //01揹包(n[i]-2^k)
{
f[j] = max(f[j], f[j-count[i]*volume[i]]+count[i]*value[i]);
}
}
}
return f[V];
}