在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;
}
}