UVA_10626

    实际上我们注意到硬币的总状态不是很多,约是500*100*50(当然这么说并不准确,后面会再算),所以我们可以把硬币的组合形式看成一个状态。

    剩下的问题就是如何转移状态,我们细算一下,发现花钱的策略不过这么几种,如果再分细一些,可以先分成有找零的和没找零的:①8个1分的;②3个1分的和1个5分的;③2个5分的找回2个1分的;④一个10分的找回2个1分的;⑤3个1分的和1个10分的找回1个5分的。其余的可能也有,但一定不会比上面五种决策更优。

    显然递推是不好搞的,所以就不妨记忆化搜索吧。但注意到,我们按上面的策略花钱时,有些硬币是会增加的,所以我们要把上限算好。首先,③和④会增加1分的数量,最多增加的数量不会超过200个,其次⑤会增加5分的数量,但不会超过50个,于是开个710*160*60的数组即可。

#include<stdio.h>
#include<string.h>
#define INF 1000000000
int f[710][160][60], C, N, X, Y, Z;
int dp(int n1, int n5, int n10)
{
int i, j, k, min = INF;
if(f[n1][n5][n10] != -1)
return f[n1][n5][n10];
if(n1 + n5 * 5 + n10 * 10 == N)
return f[n1][n5][n10] = 0;
if(n1 >= 3 && n5 >= 1)
{
k = dp(n1 - 3, n5 - 1, n10);
if(k + 4 < min)
min = k + 4;
}
if(n5 >= 2)
{
k = dp(n1 + 2, n5 - 2, n10);
if(k + 2 < min)
min = k + 2;
}
if(n1 >= 3 && n10 >= 1)
{
k = dp(n1 - 3, n5 + 1, n10 - 1);
if(k + 4 < min)
min = k + 4;
}
if(n10 >= 1)
{
k = dp(n1 + 2, n5, n10 - 1);
if(k + 1 < min)
min = k + 1;
}
if(n1 >= 8)
{
k = dp(n1 - 8, n5, n10);
if(k + 8 < min)
min = k + 8;
}
return f[n1][n5][n10] = min;
}
void solve()
{
int res;
scanf("%d%d%d%d", &C, &X, &Y, &Z);
N = X + 5 * Y + 10 * Z - C * 8;
memset(f, -1, sizeof(f));
res = dp(X, Y, Z);
printf("%d\n", res);
}
int main()
{
int t;
scanf("%d", &t);
while(t --)
{
solve();
}
return 0;
}


posted on 2011-12-05 20:35  Staginner  阅读(576)  评论(0编辑  收藏  举报