仔细推了一下利用二进制优化时使用的公式,注意n的取值范围,因为取对数,所以数组不用开那么大,100就够用。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define MAX_V 1000
#define MAX_N 1000
#define MAX_C 100000
int cashvalue[11], cashnum[11],
dp[MAX_C+10], a[MAX_C + 10];
int main()
{
int cash = 0, kinds = 0;
while(~scanf("%d", &cash))
{
int flagcash = 1, flagkinds = 1;
scanf("%d", &kinds);
if(cash == 0)
flagcash = 0;
if(flagkinds == 0)
flagkinds = 0;
if(flagkinds)
{
for(int i = 1;i <= kinds;i++)
{
scanf("%d%d", &cashnum[i], &cashvalue[i]);
}
int cnt = 0;
for(int i = 1;i <= kinds;i++)//遍历每一种
{
//按规律分堆
for(int j = 1;j <= cashnum[i];j *= 2)//n+1>2^k 2^j <= n 相当于 2^j < n+1
{
a[++cnt] = cashvalue[i] * j; //生成的这一堆的价值
cashnum[i] -= j; //用掉多少就减去多少
}
if(cashnum[i] != 0) //没有完全用光
a[++cnt] = cashvalue[i]*cashnum[i]; //额外放一堆
}
memset(dp, 0, sizeof(dp));
//此时转化为01背包 已经拥有cnt种物品 每件一个 最大容量cash
for(int i = 1;i <= cnt;i++)
{
for(int j = cash;j >= a[i];j--)
{
dp[j] = max(dp[j],dp[j-a[i]]+a[i]);
}
}
printf("%d\n", dp[cash]);
}
else //全部为0 结束输入
{
printf("0\n");
}
}
return 0;
}