1. 程式人生 > >動態規劃 HDU 2546 飯卡

動態規劃 HDU 2546 飯卡

        在acm群裡看到了這樣的一個題目,拿過來看了一下,感覺還蠻有意思的,題目大意是飯卡上有m餘額,但是學校有個規定,飯卡餘額少於5元就不能買東西,現在有n件商品,每件商品都有一個價格。要求買好商品後餘額最少。

        看到題目就想到了揹包問題,在一個m-5的揹包裡儘量裝滿東西,裝滿後放最後一個物品,解肯定在放完這個物品之後。 最直接的想法就是對放的最後這個物品進行列舉,然後用餘下的物品去裝m-5的揹包,儘量裝滿。得到的最小值就是答案。當然還要考慮餘額小於5元的情況,如果小於5元就直接輸出就行了,因為不能買東西。

        思路有了,接下去是實現,n是1000,m是1000,價格不會超過50,如果列舉的話,複雜度是n*n*m,明顯會超時。由於每件商品價格不會超過50,那麼只要統計下對應價格的商品件數就行了,然後用二進位制優化。

       帖程式碼:

#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0xFFFFFFF
int price[55]; //統計對應價格的商品件數
int dp[1001],m;

void zeroOnePack(int volume)
{
	for (int k=m-5; k>=volume; k--)
	{
		dp[k] = max(dp[k], dp[k-volume]+volume);
	}
}
int main()
{
	int  n, p;
	while (cin >> n && n )
	{
		memset(price, 0, sizeof(price));
		for (int i=0; i<n; i++)
		{
			cin >> p;
			price[p] ++ ;
		}
		cin >> m;
		if (m <5) { cout << m << endl; continue; }

		int ans = INF;
		for (int i=1; i<=50; i++)
		{
			if (!price[i]) continue;
			price[i] --;
			memset(dp, 0, sizeof(dp));
			for (int j=1; j<=50; j++)
			{
				if (!price[j]) continue;

				int total = price[j];
				for (int gs=1 ; total>=gs; gs<=1, total-= gs)
				{
					zeroOnePack(gs*j);
				}
				zeroOnePack(total*j);
			}
			int tmp = m-dp[m-5]-i;
			if (ans > tmp) ans = tmp;
			price[i] ++;
		}

		cout << ans << endl;
	}
}