bzoj1021-SHOI2008 Debt 循环的债务 dp

  • 题面描述

    • \(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\)张钞票被交换过。没过多久他们就发现这是一个很棘手的问题,于是他们找到了精通数学的你为他们解决这个难题。
  • 输入格式

    • 输入的第一行包括三个整数:\(x_1\)\(x_2\)\(x_3\),\(-10^3\leq x_1,x_2,x_3\leq 10^3\),其中 \(x_1\)代表\(Alice\)\(Bob\)的钱(如果\(x_1\)是负数,说明\(Bob\)欠了\(Alice\)的钱)以此类推
      接下来有三行,每行包括6个自然数:

      \(a_{100}\) \(a_{50}\) \(a_{20}\) \(a_{10}\) \(a_{5}\) \(a_{1}\)
      \(b_{100}\) \(b_{50}\) \(b_{20}\) \(b_{10}\) \(b_{5}\) \(b_{1}\)
      \(c_{100}\) \(c_{50}\) \(c_{20}\) \(c_{10}\) \(c_{5}\) \(c_{1}\)

      \(a_{100}\)表示\(Alice\)拥有的\(100\)元钞票张数,\(b_{50}\)表示\(Bob\)拥有的\(50\)元钞票张数,以此类推。另外,我们保证有\(a_{10}+a_5+a_1\leq 30\)\(b_{10}+b_5+b_1\leq 30\)\(c_{10}+c_5+c_1\leq 30\),而且三人总共拥有的钞票面值总额不会超过\(10^3\)

  • 输出格式

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

    • 首先看到这题,我们稍加思考便能够发现,这个问题的初状态每个人拥有的钱数,以及终状态每个人拥有的钱数是固定的。

    • 第一次看到题,我一直关注与钞票的交换上,只觉得这个交换方式十分复杂,没有办法用一种有效的方式转化。于是就\(YY\)了一个\(A^*\)出来,但过不了全部的点

      • 具体就是,把\(A^*\)的估价函数\(g(x)\)设为 不考虑每个人每种钱拥有的张数的情况下
        • \(\sum_{i=1}^3\)\(i\)个人现有\(x\)元,终状态该人有\(y\)元,从\(x\)元变到\(y\)元最少要向他人交换几张钞票
      • 这一部分可以用无限背包做
    • 但像我们最初考虑该问题是想到的初状态与终状态固定,受到物理的保守力只需考虑始、末状态,不需考虑中间过程的启发,我们其实不需要知道这些钞票是如何进行换的,我们只关心换完之后每个人所拥有的钱。

    • 这样一来,我们就会发现,每种币值的钞票相对独立,因此我们将每种币值的钞票独立开来,成为每层的状态。

    • 如此一来,我们可以设计出这样的\(dp\)方程

      • \(f_{i,x,y,z}\)表示使用前\(i\)种币值的钞票进行交换后,三个人各有\(x,y,z\)元时最少的钞票交换次数,记\(b_i\)表示第\(i\)种钞票的币值,\(k_i\)表示每个人给出\(i\)种钞票的张数(可正可负)

      • 则 他们三人之间交换第\(i​\)种钞票的总张数为\(chg=\frac{|k_1|+|k_2|+|k_3|}{2}​\)

      • \[ f_{i,x-k_1*b_i,y-k_2*b_i,z-k_3*b_i}=f_{i-1,x,y,z}+chg\\ 保证k_1+k_2+k_3=三人拥有第i种钞票的总张数 \]

    • 然后,我们不难发现\(x+y+z\)恒等三人拥有的总钱数

    • 因此我们可以降一维变成

      • \[ f_{i,x-k_1*b_i,y-k_2*b_i}=f_{i-1,x,y}+chg\\ k_3=三人拥有第i种钞票的总张数-k_1-k_2\\ chg=\frac{|k_1|+|k_2|+|k_3|}{2} \]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1e3+5;
const int b[10]={0,100,50,20,10,5,1};
int mon[4][10];
int x1,x2,x3,s[4];
int sum;
int f[7][MAXN][MAXN];
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",&mon[i][j]);
            sum+=mon[i][j]*b[j];
        }
    }
//  cout<<"done"<<endl;
    for (int i=1;i<=3;i++){
        for (int j=1;j<=6;j++) s[i]+=mon[i][j]*b[j];
    }
    if (s[1]-x1+x3>sum||s[2]+x1-x2>sum||s[3]-x3+x2>sum) return printf("impossible\n"),0;
    memset(f,63,sizeof(f)); f[0][s[1]][s[2]]=0;
    for (int i=1;i<=6;i++){
        for (int j=0;j<=sum;j++){
            for (int k=0;k+j<=sum;k++){
                if (f[i-1][j][k]>=1e9) continue;
                int cnt=mon[1][i]+mon[2][i]+mon[3][i];
                for (int x=0;x<=cnt;x++){
                    for (int y=0;y+x<=cnt;y++){
                        int z=cnt-x-y;
                        int lx=mon[1][i]-x;
                        int ly=mon[2][i]-y;
                        int lz=mon[3][i]-z;
                        int chg=(abs(lx)+abs(ly)+abs(lz))/2;
                        if (j-lx*b[i]+k-ly*b[i]>sum) continue;
                        if (j<lx*b[i]||k<ly*b[i]) continue;
                        f[i][j-lx*b[i]][k-ly*b[i]]=min(f[i][j-lx*b[i]][k-ly*b[i]],f[i-1][j][k]+chg);
                    }
                }
            }
        }
    }
    int ans=f[6][s[1]-x1+x3][s[2]-x2+x1];
    if (ans>=1e9) printf("impossible\n");
    else printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/shjrd-dlb/p/10760167.html