[bzoj1021][SHOI2008]Debt 循环的债务 (动态规划)

时间:2023-03-09 05:58:57
[bzoj1021][SHOI2008]Debt 循环的债务 (动态规划)

Description

Alice、 Bob和Cynthia总是为他们之间混乱的债务而烦恼,终于有一天,他们决定坐下来一起解决这个问题。不过,鉴别钞票的真伪是一件很麻烦的事情,于是他 们决定要在清还债务的时候尽可能少的交换现金。比如说,Alice欠Bob 10元,而Cynthia和他俩互不相欠。现在假设Alice只有一张50元,Bob有3张10元和10张1元,Cynthia有3张20元。一种比较直 接的做法是:Alice将50元交给Bob,而Bob将他身上的钱找给Alice,这样一共就会有14张钞票被交换。但这不是最好的做法,最好的做法 是:Alice把50块给Cynthia,Cynthia再把两张20给Alice,另一张20给Bob,而Bob把一张10块给C,此时只有5张钞票被 交换过。没过多久他们就发现这是一个很棘手的问题,于是他们找到了精通数学的你为他们解决这个难题。

Input

输 入的第一行包括三个整数:x1、x2、x3(-1,000≤x1,x2,x3≤1,000),其中 x1代表Alice欠Bob的钱(如果x1是负数,说明Bob欠了Alice的钱) x2代表Bob欠Cynthia的钱(如果x2是负数,说明Cynthia欠了Bob的钱) x3代表Cynthia欠Alice的钱(如果x3是负数,说明Alice欠了Cynthia的钱)接下来有三行,每行包括6个自然数: a100,a50,a20,a10,a5,a1 b100,b50,b20,b10,b5,b1 c100,c50,c20,c10,c5,c1 a100表示Alice拥有的100元钞票张数,b50表示Bob拥有的50元钞票张数,以此类推。另外,我们保证有 a10+a5+a1≤30,b10+b5+b1≤30,c10+c5+c1≤30,而且三人总共拥有的钞票面值总额不会超过1,000。

Output

如果债务可以还清,则输出需要交换钞票的最少张数;如果不能还清,则输出“impossible”(注意单词全部小写,输出到文件时不要加引号)。

Sample Input

输入一
10 0 0
0 1 0 0 0 0
0 0 0 3 0 10
0 0 3 0 0 0
输入二
-10 -10 -10
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0

Sample Output

输出一
5
输出二
0

HINT

对于100%的数据,x1、x2、x3 ≤ |1,000|。

分析

唔……好像没什么难的吧,设f(i,j)表示从初始状态到“A手中持有i元,B手中持有j元,C手中持有Sum-i-j元”这一状态需要的最少操作数,然后dp即——

——等等……这里转移方向是加减两个方向都可以啊?不可能是dp吧……?

于是我就在网上看了巨神们的题解……

啊……看来又是我蠢了……

他们的思路大概是这样的……考虑这样一个事实:对于同样一种面值的纸币,最优解中不可能出现“A给B,B给C”这样的循环交付,否则我们只要让A直接给C就可以得到更优的解。于是我们就可以从面值来枚举,对每种面值分别dp更新答案。

代码应该也比较好懂吧,只不过我写得似乎太冗长了……

[bzoj1021][SHOI2008]Debt 循环的债务 (动态规划)[bzoj1021][SHOI2008]Debt 循环的债务 (动态规划)
    #include <cstdio>
 #include <cstring>
 #include <cstdlib>
 #include <ctime>
 #include <cctype>
 #include <algorithm>
  template<                    x = ch -       -       }
  , INF = ] = {, , , , , };
  
 ][], tot[], Cur[], Tar[], Sum, f[][maxn][maxn];  inline           getd(a), getd(b), getd(c);
     ;i < ;++i);j >= ;--j){
         getd(cnt[i][j]);
         Cur[i] += cnt[i][j] * val[j];
         tot[j] += cnt[i][j];
     }
     Sum = Cur[] + Cur[] + Cur[];
     Tar[] = Cur[] - a + c;
     Tar[] = Cur[] - b + a;
  
     ] <  || Tar[] <  || Sum - Tar[] - Tar[] < ){
         printf(         exit();
     }
 }
  
  #include <cmath>
  
 inline      ;
               ;i <= Sum;++i)memset(f[][i],      f[][Cur[]][Cur[]] = ;
     ;i < ;++i){ , las = cur ^ ;
         ;j <= Sum;++j)memset(f[cur][j],          ;j <= Sum;++j){
             t = Sum - j;
             ;k <= t;++k){                  UPD(f[cur][j][k], f[las][j][k]);
                 ;a <= tot[i];++a){
                     tmp = tot[i] - a;
                     ;b <= tmp;++b){ ][i], db = b - cnt[][i];
                         cnta = j + da * val[i], cntb = k + db * val[i];
                          || cntb <  || cnta + cntb > Sum)                         UPD(f[cur][cnta][cntb], f[las][j][k] + (abs(da) + abs(db) + abs(da + db)) / );
                     }
                 }
             }
         }
     }
     ]][Tar[]] == INF)printf(     ]][Tar[]]);
 }
  
  #ifdef DEBUG
     freopen(      freopen(     freopen(      init();
     work();
  
 #ifdef DEBUG
     printf(      ;
 }

动态规划