BZOJ 1021 :[SHOI2008]Debt 循环的债务 (DP)
真是蒟蒻,已经连续几题沦落到不看题解写不出的地步了,最近不太想写题了,学习新的算法吧
用f[i][j][k]表示算到第i种钱,乙有j元,丙有k元的最小方案数,记录可以转移的方案以及去掉不可以转移的方案就行了
CODE:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int f[7][1010][1010],t[4][7],sum[4];
int a[7]={0,1,5,10,20,50,100};
int x[4];
int c[4];
struct node{
int x,y;
}q[101000],k[101000];
bool b[7][1010][1010];
int check(int x,int y,int z,int ans){
if (b[x][y][z]) {
f[x][y][z]=min(f[x][y][z],ans);
return 0;
}else {
b[x][y][z]=1;
f[x][y][z]=ans;
}
if (x==6) return 0;
if (x!=3){
if (abs(sum[2]-y)%a[x+1]!=0) return 0;
if (abs(sum[3]-z)%a[x+1]!=0) return 0;
if (abs(sum[2]+sum[3]-y-z)%a[x+1]!=0) return 0;
}
k[0].x++;k[k[0].x]=(node){y,z};
return 0;
}
int solve(int i,int j,int x,int y,int z){
for (int k=0;k<=t[y][i];k++)
for (int l=0;l<=t[z][i];l++) {
c[x]=k+l;c[y]=-k;c[z]=-l;
check(i,q[j].x+c[2]*a[i],q[j].y+c[3]*a[i],f[i-1][q[j].x][q[j].y]+k+l);
}
for (int k=0;k<=t[x][i];k++)
for (int l=0;l+k<=t[x][i];l++){
c[x]=-(k+l);c[y]=k;c[z]=l;
check(i,q[j].x+c[2]*a[i],q[j].y+c[3]*a[i],f[i-1][q[j].x][q[j].y]+k+l);
}
return 0;
}
int main(){
scanf("%d%d%d",&x[1],&x[2],&x[3]);
for (int i=1;i<=3;i++)
for (int j=1;j<=6;j++) {
scanf("%d",&t[i][6-j+1]);
sum[i]+=t[i][6-j+1]*a[6-j+1];
}
q[0].x=1;
q[1]=(node){sum[2],sum[3]};
sum[2]+=x[1]-x[2];
sum[1]+=x[3]-x[1];
sum[3]+=x[2]-x[3];
for (int i=1;i<=6;i++){
k[0].x=0;
for (int j=1;j<=q[0].x;j++) {
solve(i,j,1,2,3);
solve(i,j,2,1,3);
solve(i,j,3,1,2);
}
swap(k,q);
}
if (b[6][sum[2]][sum[3]]) printf("%d",f[6][sum[2]][sum[3]]);
else printf("impossible");
return 0;
}