题目链接
1.二进制代码优化放进循环中的代码
for (i = 1; i <= n; i++)
{
for (k = 1; k <= num[i]; k *= 2)
{
for (j = vol; j >= k * val[i]; j--)
{
dp[j] = min(dp[j], dp[j - k * val[i]] + k);
}
num[i] -= k;
}
for (j = vol; j >= num[i] * val[i]; j--)
{
dp[j] = min(dp[j], dp[j - num[i] * val[i]] + num[i]);
}
}
2.鸽笼原理确定付钱上限
付钱数不会超过maxn*maxn+m
反证法:假定付钱数超过此上限,那么说明付硬币的数量超过了maxn,根据鸽笼原理,至少有两个的和对maxn取模的值相等(这个意思应该是:至少maxn+1个硬币对maxn求余,然后余数属于[0,maxv-1]范围,肯定有至少两个硬币的余数相同的),也就是说,这部分硬币能够用更少的maxn来代替。(通俗的来说就是可以用更少的大钱换很多的小钱,金币数就少了)
3.代码部分
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int Mt = 10005 + 105 * 105;
#define inf 0x3f3f3f3f
int val[105];
int num[105];
int c[10005];
int maxn = 0;
int dp1[Mt], dp2[Mt];
int main()
{
int n, m;
while (cin >> n >> m)
{
memset(dp1, inf, sizeof(dp1));
memset(dp2, inf, sizeof(dp2));
dp1[0] = dp2[0] = 0;
for (int i = 1; i <= n; i++)
{
cin >> val[i];
maxn = max(maxn, val[i]);
}
for (int i = 1; i <= n; i++) cin >> num[i];
int vol = maxn * maxn + m;
int i, j, k;
for (i = 1; i <= n; i++)
{
for (k = 1; k <= num[i]; k *= 2)
{
for (j = vol; j >= k * val[i]; j--)
{
dp1[j] = min(dp1[j], dp1[j - k * val[i]] + k);
}
num[i] -= k;
}
for (j = vol; j >= num[i] * val[i]; j--)
{
dp1[j] = min(dp1[j], dp1[j - num[i] * val[i]] + num[i]);
}
}
for (int i = 1; i <= n; i++)
{
for (int j = val[i]; j <= vol; j++)
dp2[j] = min(dp2[j], dp2[j - val[i]] + 1);
}
int ans = inf;
for (int i = m; i <= vol; i++)
{
ans = min(ans, dp1[i] + dp2[i - m]);
}
if (ans == inf)
cout << -1 << endl;
else
cout << ans << endl;
}
return 0;
}