题解:P4026 [SHOI2008] 循环的债务
前言
需要脑子题。
思路分析
首先受到样例二的启发,如果我们决策这个交换纸币的过程也太困难了。所以需要换一种刻画方式。
考虑把所有纸币都放在桌子上,三个人再分配。代价就是分配之前和分配之后,每种面值的纸币的差。
这样就好 DP 了。
设 \(f_{i,a,b}\) 表示前 \(i\) 中面值的纸币,第一个人有 \(a\) 元,第二个人有 \(b\) 元。第三个人有多少钱可以计算出来。
每次转移时,枚举第一个人拿几张,第二个人拿几张,第三个人拿几张,背包一下就行。
复杂度不高,不想精细计算了。
代码实现
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int x1,x2,x3,a[7],b[4][7],f[7][1001][1001],tot[7],num[7],sum[7],ans;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>x1>>x2>>x3;
a[1]=100,a[2]=50,a[3]=20,a[4]=10,a[5]=5,a[6]=1;
for(int i=1;i<=3;i++){
for(int j=1;j<=6;j++){
cin>>b[i][j];
sum[i]+=b[i][j]*a[j];
tot[j]+=b[i][j]*a[j];
num[j]+=b[i][j];
}
}
for(int i=1;i<=6;i++){
tot[i]+=tot[i-1];
}
sum[1]+=x3-x1;
sum[2]+=x1-x2;
sum[3]+=x2-x3;
if(sum[1]<0 || sum[2]<0 || sum[3]<0){
cout<<"impossible";
return 0;
}
memset(f,0x3f,sizeof(f));
f[0][0][0]=0;
for(int i=1;i<=6;i++){
for(int a1=0;a1<=sum[1] && a1<=tot[i];a1++){
for(int a2=0;a2<=sum[2] && a2<=tot[i] && a1+a2<=tot[i];a2++){
int a3=tot[i]-a1-a2;
if(a3<0 || a3>sum[3]) continue;
for(int l1=0;l1<=num[i] && l1*a[i]<=a1;l1++){
for(int l2=0;l2<=num[i] && l1+l2<=num[i] && l2*a[i]<=a2;l2++){
int l3=num[i]-l1-l2;
if(l3<0 && a3<l3*a[i]) continue;
f[i][a1][a2]=min(f[i][a1][a2],f[i-1][a1-l1*a[i]][a2-l2*a[i]]+(abs(l1-b[1][i])+abs(l2-b[2][i])+abs(l3-b[3][i]))/2);
}
}
}
}
}
ans=f[6][sum[1]][sum[2]];
if(ans>inf) cout<<"impossible";
else cout<<ans;
return 0;
}

浙公网安备 33010602011771号