在acm羣裏看到了這樣的一個題目,拿過來看了一下,感覺還蠻有意思的,題目大意是飯卡上有m餘額,但是學校有個規定,飯卡餘額少於5元就不能買東西,現在有n件商品,每件商品都有一個價格。要求買好商品後餘額最少。
題目鏈接 HDU 2546 飯卡
看到題目就想到了揹包問題,在一個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;
}
}