1. 程式人生 > >poj-1014 多重揹包問題

poj-1014 多重揹包問題

    題意:有一堆大理石,每個石頭的價值在1-6之間,每種價格的石頭有多個。現在要求將這堆石頭分成兩份,使得兩份的總價值相同,回答是否存在一種方法將可以實現劃分。

    這是一道多重揹包問題,多重揹包問題的做法可以是先將它轉化為01揹包,再用01揹包問題的方法繼續求解。轉化的思路是,將一個價格下的幾件物品,組合成多個價格下個一件物品。比如價格2下原來有7件物品(二進位制111),可以分為有1件價格1的物品(001),1件價格2的物品(010),1件價格4的物品(100),可以看出這裡用到了二進位制的操作。 這麼操作能夠實現每種物品只有一個,只有1拿和0不拿兩種情況。且可以組合成原多重揹包時的任意組合。 

    程式中,t[i]表示有價值i的大理石數量,現在要把他分成編號1-count-1的大理石w[i]記錄編號i的大理石的價值,轉化後,每個編號下的物品只有一個。

for(int i=1;i<=6;i++)
	{
		int c=1;
		while(t[i]-c>0)
		{
			w[count]=c*i;
			t[i]-=c;
			c=c<<1;
			count ++;
		}
		w[count]=t[i]*i;
		count++;
	}
    本題與常見的01揹包略有不同的是,本題沒有物品的體積,所以可以當作物品的體積和價值一樣來做。狀態轉移方程就是,dp[i][j] = MAX( dp[i][j-w[i]]+w[i] , dp[i-1][j] ),做題時再把dp陣列壓縮成一維,便有了如下的程式
for(int i=1;i<=count-1;i++)
	{
		for(int j=sum;j>=w[i];j--)
		{
			dp[j] = max(dp[j],dp[j-w[i]]+w[i]);
		}
	}

01 揹包

有n 種不同的物品,每個物品有兩個屬性,size 體積,value 價值,現在給一個容量為 w 的揹包,問最多可帶走多少價值的物品。 

int f[w+1];   //f[x] 表示揹包容量為x 時的最大價值
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]);

完全揹包

如果物品不計件數,就是每個物品不只一件的話,稍微改下即可

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]);
最後是我AC的完整程式碼
#include<iostream>
#include<cstring>
#include<math.h>
using namespace std;
int t[10],dp[140010],w[140010];
int DP_fun(int sum)
{
	memset(dp,0,sizeof(dp));
	int count = 1;
	for(int i=1;i<=6;i++)
	{
		int c=1;
		while(t[i]-c>0)
		{
			w[count]=c*i;
			t[i]-=c;
			c=c<<1;
			count ++;
		}
		w[count]=t[i]*i;
		count++;
	}
	
	for(int i=1;i<=count-1;i++)
	{
		for(int j=sum;j>=w[i];j--)
		{
			dp[j] = max(dp[j],dp[j-w[i]]+w[i]);
		}
	}
	return dp[sum];
}
int main()
{
	for(int ID=1;1;ID++)
	{
		int flag = 0;
		int sum=0;
		for(int i=1;i<=6;i++)
		{
			cin >> t[i];
			if(t[i]>0)
			{
				flag = 1;
				sum += t[i]*i;
			}
		}
		if(flag == 0)
		{
			break;
		}
		cout << "Collection #"<<ID<<":"<<endl;
		if(sum%2 == 1)
		{
			cout << "Can't be divided."<<endl<<endl;
			continue;
		}
		if(sum/2==DP_fun(sum/2))
		{
			cout << "Can be divided."<<endl<<endl;
		}
		else
		{
			cout << "Can't be divided."<<endl<<endl;
		}
	}
	return 0;
}