poj 1276 多重揹包問題

題意:

給出N種貨幣,每種貨幣有a[i]張,

對於給定的錢數X,求出貨幣可以拼出的最接近於並且小於等於X的數值


解法:

把錢的上限X看作體積,並且每種貨幣的面值既是揹包問題裏面的“體積”也是“價值

相對於0-1揹包問題,該問題屬於多重揹包問題(每組個數有限數量不一),

比較好寫的解法是轉換爲0-1揹包,該方法的時間複雜度是O(V*Σn[i]),對於1276這道題會TLE

分組的解法:


對於每一組物品

我們考慮把第i種物品換成若干件物品,使得原問題中第i種物品可取的每種策略——取0..n[i]件——均能等價於取若干件代換以後的物品。

另外,取超過n[i]件的策略必不能出現。

方法是:將第i種物品分成若干件物品,其中每件物品有一個係數,這件物品的費用和價值均是原來的費用和價值乘以這個係數。

使這些係數分別爲1,2,4,...,2^(k-1),n[i]-2^k+1,且k是滿足n[i]-2^k+1>0的最大整數。

例如,如果n[i]爲13,就將這種物品分成係數分別爲1,2,4,6的四件物品。

設k爲所求的k,num爲給定的該組的個數,下面的程序計算k的值

			int k = 0;
			int temp = 1;
			while (1)
			{
				if (temp - 1 >= num)
					break;
				temp *= 2;
				k++;
			}
			k = k - 1;

計算得出k以後對該組重新分配的物品計算對應的係數,設該組物品價值爲val,結果存入fee數組,

第一個係數必須爲1,後面的按照上述方法計算

			//分組
			int at = 1;
			for (i = 0; i < k + 1; i++)
			{

				if (i != 0)
				{
					if (i == k)
					{
						at = num - at * 2 + 1;
					}
					else
						at *= 2;
				}
				fee[cnt++] = at * val;
			}

之後重新運用0-1經典揹包和滾動數組方法求解

#include <iostream>
#include <vector>
#include <map>
#include <list>
#include <set>
#include <deque>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <iomanip>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
#include <queue>
using namespace std;

///宏定義
const int  INF = 990000000;
const int MAXN = 100000;
const int maxn = 110000 ;
///全局變量 和 函數
int max(int a, int b)
{
	return a > b ? a : b;
}

//int T;
int N;
int V;
int f[maxn];
int fee[maxn];
int main()
{
	///變量定義
	int i, j, m;
	while(scanf("%d", &V) != EOF)
	{
		scanf("%d", &N);
		int totnum = 0;
		int cnt = 0;
		for (int kk = 0;kk < N; kk++)
		{
			int num, val; //該組的個數和該組的價值
			scanf("%d %d", &num, &val);
			if (num == 0)
				continue;
			int k = 0;
			int temp = 1;
			while (1)
			{
				if (temp - 1 >= num)
					break;
				temp *= 2;
				k++;
			}
			k = k - 1;
			//分組
			int at = 1;
			for (i = 0; i < k + 1; i++)
			{

				if (i != 0)
				{
					if (i == k)
					{
						at = num - at * 2 + 1;
					}
					else
						at *= 2;
				}
				fee[cnt++] = at * val;
			}
		}

		memset(f, 0, sizeof(f));
		for (i = 0; i < cnt; i++)
		{
			for (j = V; j >= 0; j--)
			{
				if (j >= fee[i])
				{
					if (f[j] < f[j - fee[i]] + fee[i])
					{
						f[j] = f[j - fee[i]] + fee[i];
					}
				}
			}
		}

		int ans = f[V];
		printf("%d\n", ans);
	}

	///結束
	return 0;
}


分組的可重用程序

int fee[maxn];
int cnt;
void init(int n)
{
	int i, j;
	cnt = 0;
	for (int kk = 0; kk < n; kk++) //進行分組,存入fee數組
	{
		int num = m;
		int k = 0;
		int temp = 1;
		while (1)
		{
			if (temp - 1 >= m)
				break;
			temp *= 2;
			k++;
		}
		k = k - 1;
		
		//分組
		int at = 1;
		for (i = 0; i < k + 1; i++)
		{
			
			if (i != 0)
			{
				if (i == k)
				{
					at = num - at * 2 + 1;
				}
				else
					at *= 2;
			}
			fee[cnt++] = at * kk;
		}
	}
}



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