揹包問題例題總結

Tiny_W發表於2018-03-31

揹包問題之前學了,不過現在又忘得差不多了(可惜之前沒有寫部落格總結),現在結合例題重新複習一下。

例1:

揹包1

Time Limit: 2000/1000ms (Java/Others)

Problem Description:

   有 n 個重量和價值分別為Wi,Vi的物品,現從這些物品中挑選出總量不超過 W 的物品,求所有方案中價值總和的最大值。

Input:

輸入包含多組測試用例,每一例的開頭為兩位整數 n、W(1<=n<=10000,1<=W<=1000)
,接下來有 n 行,每一行有兩位整數 Wi、Vi(1<=Wi<=10000,1<=Vi<=100)。

Output:

輸出為一行,即所有方案中價值總和的最大值。

Sample Input:

3 4
1 2
2 5
3 7

Sample Output:

9

附上AC程式碼加解析:

#include <cstdio>  
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <list>
#include <map>
#include <stack>
#include <queue>
using namespace std;
#define long long ll
int dp[1111];//建立一個dp陣列,用來儲存剩下w重量可用時所擁有的價值最大w,即dp[w] = v
int main()
{
	int n,w;
	int wi[11111];
	int vi[11111];
	while(cin >> n >> w)
	{
		memset(dp,0,sizeof(dp));
		for(int i = 0;i < n;i++)
			scanf("%d%d",&wi[i],&vi[i]);
		for(int i = 0;i < n;i++)//列舉出每個物品
			for(int j = w;j >= wi[i];j--)//將剩餘重量從大到小排列,不能從小到大列舉,因為這樣會導致一個物品可能被使用多次
					dp[j] = max(dp[j],dp[j-wi[i]]+vi[i]);
		printf("%d\n",dp[w]);
	}
	return 0;
}



例二:

揹包2

Time Limit: 2000/1000ms (Java/Others)

Problem Description:

 有 n 個重量和價值分別為Wi,Vi的物品,現從這些物品中挑選出總量剛好為 W 的物品
,求所有方案中價值總和的最大值。

Input:

輸入包含多組測試用例,每一例的開頭為兩位整數 n、W(1<=n<=10000,1<=W<=1000)
,接下來有 n 行,每一行有兩位整數 Wi、Vi(1<=Wi<=10000,1<=Vi<=100)。

Output:

輸出為一行,即所有方案中價值總和的最大值。若不存在剛好填滿的情況,輸出“-1”。

Sample Input:

3 4
1 2
2 5
2 1
3 4
1 2
2 5
5 1

Sample Output:

6
-1

這題就是上題的改版,附上AC程式碼:

#include <cstdio>  
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <list>
#include <map>
#include <stack>
#include <queue>
using namespace std;
#define long long ll
int dp[11111];
int main()
{
	int n,w;
	while(~scanf("%d%d",&n,&w))
	{
		memset(dp,-1,sizeof(dp));//所有值設定為-1,當dp[i]等於-1時,表示暫時沒有出現剩餘i重量的情況
		dp[0] = 0;//設定一個初始值為0
		int wi[n],vi[n];
		for(int i = 0;i < n;i++)
			scanf("%d%d",&wi[i],&vi[i]);
		for(int i = 0;i < n;i++)
			for(int j = w;j >= wi[i];j--)
				if(dp[j-wi[i]] != -1)
					dp[j] = max(dp[j],dp[j-wi[i]]+vi[i]);
		printf("%d\n",dp[w]);
	}	
	return 0;
}


例3:

揹包3

Time Limit: 2000/1000ms (Java/Others)

Problem Description:

  有 n 種(每一種有無數個)重量和價值分別為Wi,Vi的物品,現從這些物品中挑選出總

量不超過 W 的物品,求所有方案中價值總和的最大值。

Input:

輸入包含多組測試用例,每一例的開頭為兩位整數 n、W(1<=n<=10000,1<=W<=1000)

,接下來有 n 行,每一行有兩位整數 Wi、Vi(1<=Wi<=10000,1<=Vi<=100)

Output:

輸出為一行,即所有方案中價值總和的最大值。

Sample Input:

3 4
1 2
2 5
3 7
3 5
2 3
3 4
4 5

Sample Output:

10
7

這題跟第一題類似,如果理解第一題為什麼是把剩餘重量從大到小排列,就能理解為什麼只要這題從小到大排列即可AC

附上AC程式碼:

#include <cstdio>  
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <list>
#include <map>
#include <stack>
#include <queue>
using namespace std;
#define long long ll
int dp[11111];
int main()
{
	int n,w;
	while(~scanf("%d%d",&n,&w))
	{
		memset(dp,0,sizeof(dp));
		int wi[n],vi[n];
		for(int i = 0;i < n;i++)
			scanf("%d%d",&wi[i],&vi[i]);
		for(int i = 0;i < n;i++)
			for(int j = wi[i];j <= w;j++)//剩餘重量從小到大就能重複利用每個物品,從大到小就只能利用一次
				dp[j] = max(dp[j],dp[j-wi[i]]+vi[i]);
		printf("%d\n",dp[w]);
	}
	return 0;
}

例4:

揹包4

Time Limit: 2000/1000ms (Java/Others)

Problem Description:

有 n 個重量和價值分別為Wi,Vi的物品,現從這些物品中挑選出總量不超過 W 的物品,求所有方案中價值總和的最大值。

Input:

輸入包含多組測試用例,每一例的開頭為兩位整數 n、W;接下來有 n 行,每一行有兩位整數 Wi、Vi
其中:
1<=n<=100
1<=W<=1000,000,000
1<=Wi<=10,000,000   
1<=Vi<=100。 

Output:

輸出為一行,即所有方案中價值總和的最大值。

Sample Input:

4 5
2 3
1 2
3 4
2 2
4 10000000
2 3
2 2
3 3
1 2

Sample Output:

7
10

這題需要轉換下思維,因為按照前面的方法,重量太大,肯定會超時,不過,相對而言,價值卻變小了。如果理解了前面幾題,並轉換了思維,這題應該不難。

附上AC程式碼:

#include <cstdio>  
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <list>
#include <map>
#include <stack>
#include <queue>
using namespace std;
#define long long ll
int dp[11111];
int main()
{

	int n,w;
	while(~scanf("%d%d",&n,&w))
	{
		for(int i = 0;i < 10005;i++)
			dp[i] = 1111111111;
		dp[0] = 0;
		int wi[n],vi[n];
		for(int i = 0;i < n;i++)
			scanf("%d%d",&wi[i],&vi[i]);
		for(int i = 0;i < n;i++)
			for(int j = 10005;j >= vi[i];j--)
				if(dp[j-vi[i]] != 1111111111 && dp[j-vi[i]]+wi[i] <= w)
					dp[j] = min(dp[j],dp[j-vi[i]]+wi[i]);
		for(int i = 10004;i >= 0;i--)
			if(dp[i] != 1111111111)
			{
				printf("%d\n",i);
				break;
			}
	}
	return 0;
}


相關文章