题目链接: 点击打开链接
题目大意: Farmer John买东西,要求付的金币数加找回的金币数总和最小
思路: 动态规划,多重背包,完全背包
分析:
John的金币数是有限的,我们可以用多重背包算给定数额下John最少能付多少金币。卖家金币数是无限的,我们可以用完全背包算给定找钱数下卖家最少能付多少金币。最后遍历两个dp数组,算出最小的金币数就行了。我用dp_recei数组存放找钱金币数,用dp_give计算John给的金币数。
背包上限不太好定,开始是把John所拥有的金钱总额算出来作为上限,TLE了,上网看了大牛代码才发现可以设的更小,最后设成T+10000。
代码:
#include <cstdio>
#include <memory.h>
#include <algorithm>
using namespace std;
const int maxv = 20000;
const int maxn = 120;
const int INF = 9999999;
int value[maxn], amount[maxn], dp_recei[maxv], dp_give[maxv], n, V, T;
void ZeroOnePack(int cost, int value, int dp[])
{
for (int i = V; i >= cost; --i)
dp[i] = min(dp[i], dp[i - cost] + value);
}
void ComPack(int cost, int value, int dp[])
{
for (int i = cost; i <= V; ++i)
dp[i] = min(dp[i], dp[i - cost] + value);
}
void MulPack(int cost, int value, int amount, int dp[])
{
if (cost * amount > V) {
ComPack(cost, value, dp);
return;
}
for (int i = 1; i <= amount; i <<= 1) {
ZeroOnePack(cost * i, value * i, dp);
amount -= i;
}
ZeroOnePack(cost * amount, value * amount, dp);
}
int solve()
{
for (int i = 1; i <= V; ++i) dp_give[i] = INF;
dp_give[0] = 0;
for (int i = 1; i <= n; ++i)
MulPack(value[i], 1, amount[i], dp_give);
V -= T;
for (int i = 1; i <= V; ++i) dp_recei[i] = INF;
dp_recei[0] = 0;
for (int i = 1; i <= n; ++i)
ComPack(value[i], 1, dp_recei);
int min_cent = INF;
for (int i = 0; i <= V; ++i)
if (dp_recei[i] + dp_give[i + T] < min_cent)
min_cent = dp_recei[i] + dp_give[i + T];
if (min_cent == INF) return -1;
else return min_cent;
}
int main()
{
scanf("%d %d", &n, &T);
V = T + 10000;
for (int i = 1; i <= n; ++i) scanf("%d", &value[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &amount[i]);
printf("%d\n", solve());
return 0;
}