揹包問題(動態規劃)

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];
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章