题解 P4026 【[SHOI2008]循环的债务】
恶心的dp
我们考虑这个问题其实就是一开始A,B,C分别有sa,sb,sc的钱,总共n元,经过一波py,目标状态的A,B,C分别有ea,eb,ec的钱。问最少需要交换几张钱。
我们一种面额一种面额的考虑,显然不同面额之间互不影响.(而且你考虑的顺序其实可以是任意的,所以你可以按照张数从小到大来做。)
我们考虑dp,f[k][i][j],表示考虑前k种面额,A现在有i元,B现在有j元最少需要交换几张。
(显然此时C有n-i-j元,不用记录这一维状态)我们现在考虑第k+1种面额,枚举py之后A有a张,B有b张,c有tot-a-b张,能转移到的状态可以直接计算,交换张数也可以直接计算。
code
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 1050
#define maxm 10
using namespace std;
int X1,X2,X3,tot,cnt[maxm],sum[maxn];
int num[maxm][maxm],val[maxm]={0,100,50,20,10,5,1};
int f[maxm][maxn][maxn],inf,ans;
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",&num[i][j]);
sum[i]+=num[i][j]*val[j];
tot+=num[i][j]*val[j];
cnt[j]+=num[i][j];
}
}
memset(f,127,sizeof(f));
f[0][sum[1]][sum[2]]=0;inf=ans=f[1][1][1];
for(int i=1;i<=6;i++) for(int j=0;j<=tot;j++) for(int k=0;k+j<=tot;k++)
if(f[i-1][j][k]!=inf) {for(int x1=0;x1<=cnt[i];x1++)
for(int x2=0;x1+x2<=cnt[i];x2++){
int now1=j-(num[1][i]-x1)*val[i];
int now2=k-(num[2][i]-x2)*val[i];
int x3=cnt[i]-x1-x2;
if(now1>=0&&now2>=0&&now1+now2<=tot){
int w=abs(num[1][i]-x1)+abs(num[2][i]-x2)+abs(num[3][i]-x3);
f[i][now1][now2]=min(f[i][now1][now2],f[i-1][j][k]+(w>>1));
}
}
}
int S1=sum[1]-X1+X3,S2=sum[2]-X2+X1,S3=sum[3]-X3+X2;
for(int i=0;i<=6;i++)ans=min(ans,f[i][S1][S2]);
if(S1<0||S2<0||S3<0||ans==inf)printf("impossible");
else printf("%d",ans);
return 0;
}