题解P4026 [SHOI2008]循环的债务
一道比较难想的DP
可以想到,每个面额的钞票之间是可以独立计算的
那么就可以对于每一种面额的钞票单独考虑,接下来就是状态的定义了
有一种比较好想的状态就是定义 \(f_{i,x,y,z}\) 表示前 \(i\) 种钞票, 每个人分别有多少钱,但很明显会超空间.
仔细观察可以发现,钞票的总面额是不变的,那么状态就可以优化掉一维,只需要表示出两个人所有的面额就可以计算出第三个人
然后状态的转移就比较简单了,直接枚举钞票的数量就行了
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#define INF 0x3f3f3f3f
using namespace std;
int f[7][1005][1005];
int x1, x2, x3, sum;
int n[4][7], s[4], cnt[7];
int val[7] = {0, 100, 50, 20, 10, 5, 1};
int main()
{
scanf("%d%d%d", &x1, &x2, &x3);
for (int i = 1; i <= 3; i++)
for (int j = 1; j <= 6; j++)
{
scanf("%d", &n[i][j]);
sum += n[i][j] * val[j], s[i] += n[i][j] * val[j];
cnt[j] += n[i][j];
}
memset(f, 0x3f, sizeof(f));
f[0][s[1]][s[2]] = 0;
for (int i = 1; i <= 6; i++)
{
for (int j = 0; j <= sum; j++)
{
for (int k = 0; k + j <= sum; k++)
{
if (f[i - 1][j][k] == INF)
continue;
f[i][j][k] = f[i - 1][j][k];
for (int t1 = 0; t1 <= cnt[i]; t1++)
{
for (int t2 = 0; t2 + t1 <= cnt[i]; t2++)
{
int now1 = j - (n[1][i] - t1) * val[i];
int now2 = k - (n[2][i] - t2) * val[i];
int t3 = cnt[i] - t1 - t2;
if (now1 >= 0 && now2 >= 0 && sum >= now1 + now2)
{
int t = abs(n[1][i] - t1) + abs(n[2][i] - t2) + abs(n[3][i] - t3);
f[i][now1][now2] = min(f[i][now1][now2], f[i - 1][j][k] + t / 2);//除以2是因为是交换
}
}
}
}
}
}
int ans = INF, s1 = s[1] - x1 + x3, s2 = s[2] - x2 + x1;
for (int i = 0; i <= 6; i++)
ans = min(ans, f[i][s1][s2]);
if (s1 < 0 || s2 < 0 || sum < s1 + s2 || ans == INF)
printf("impossible");
else
printf("%d", ans);
return 0;
}