[BZOJ]1021 循环的债务

来源:互联网 发布:上淘宝网怎么注册 编辑:程序博客网 时间:2024/05/01 07:36

[BZOJ]1021

题意:

  给出三个人之间的欠钱关系和他们各自持有的钱币种类和个数,求能不能各自把钱还清,如果不能输出”Impossible”,如果能输出最小的给钱张数。

题解:

  Dp太神了!(弱菜自带遇Dp必跪flag)总之就是Dp。  
  可以按钱币种类划分阶段,那么方程可以为dp[i][j][k]表示用上了前i种钱币,达到了让第一个人有j块钱,第二个人有k块钱(因为总的钱数是一定的,确定了这两个第三个就确定了)所用的最小交换次数。那么最后的Ans就是dp[6][Tj][Tk]这样。TjTk可以分别用Sab+cSbc+a得到Sa是初始时给出的第一个人所拥有的钱数,Sb同理(即初始的减去给别人的加上别人给他的),最后就考虑如何转移了。
  初始值dp[0][Sa][Sb]=0其它都是INF即最大值,转移就是枚举所有的情况像背包(?)一样去转移。这里需要明确一点,如果我们已知了第一个人为了达到j钱给钱或者拿钱的张数和第二个人为了达到k钱给钱或者拿钱的张数,那么这个转移中需要统计的总共交换钱币张数就是(abs(DeltaA)+abs(DeltaB)+abs(DeltaA+DeltaB))/2,这个式子具体就是A改变的加上B改变的加上C改变的最后除去重复的。然后枚举一下jk,接着枚举一下当前钱币数目,就可以进行转移,其实这部分转移看代码比较容易理解,口述真的感人。

代码

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define INF 33686018using namespace std;const int Maxn = 1010;int val[7] = {0,100,50,20,10,5,1},cnt[Maxn][Maxn],d[Maxn],e[Maxn],sum,a,b,c,dp[3][Maxn][Maxn];int main(){    scanf("%d%d%d",&a,&b,&c);    for(int i = 1;i <= 3;i++){        for(int j = 1;j <= 6;j++){            scanf("%d",&cnt[i][j]);            sum += cnt[i][j] * val[j];//统计总钱数             d[i] += cnt[i][j] * val[j];//统计第i个人的初始总钱数             e[j] += cnt[i][j];//统计第j种钱币的总钱数         }    }    int Ta = d[1] - a + c,Tb = d[2] - b + a;    if(Ta < 0 || Tb < 0 || sum - Ta - Tb < 0){printf("impossible\n");return 0;}    memset(dp[0],2,sizeof(dp[0]));int pre = 0,now = 0;dp[0][d[1]][d[2]] = 0;    for(int i = 1;i <= 6;i++){        pre = now;now ^= 1;memset(dp[now],2,sizeof(dp[now]));        for(int j = 0;j <= sum;j++){            for(int k = 0;j + k <= sum;k++){                if(dp[pre][j][k] == INF)continue;                dp[now][j][k] = min(dp[now][j][k],dp[pre][j][k]);//相当于初值                 for(int l = 0;l <= e[i];l++){                    for(int o = 0;o + l <= e[i];o++){                        int DeltaA = l - cnt[1][i],DeltaB = o - cnt[2][i];                        int TCost1 = j + DeltaA * val[i],TCost2 = k + DeltaB * val[i];                        if(TCost1 < 0 || TCost2 < 0 || sum - TCost1 - TCost2 < 0)continue;                        dp[now][TCost1][TCost2] = min(dp[now][TCost1][TCost2],dp[pre][j][k] + (abs(DeltaA) + abs(DeltaB) + abs(DeltaA + DeltaB)) / 2);                    }                }            }        }    }    if(dp[now][Ta][Tb] == INF){printf("impossible\n");return 0;}    else printf("%d\n",dp[now][Ta][Tb]);    return 0;}
0 0
原创粉丝点击