洛谷P4026 [SHOI2008]循环的债务——题解

题目传送门
题目大意:
三人互相交换纸币使每个人都达到最终结果,试问交换纸币的最少张数。


思考过程&具体做法:
比较明显的dp,因为一共只有6种币值,考虑枚举币值,dp[i][a][b]表示枚举到第i种纸币,a的钱数为a,b的钱数为b(c的钱数为总数-a-b)的最小交换数,转移比较简单看代码吧。(我用了滚动数组写这个dp)


代码:

#include <bits/stdc++.h>
using namespace std;

int mz[6]={ 1,5,10,20,50,100 },dp[2][1010][1010],cnt[3][6],sum[3],msum[6];
int tot,x1,x2,x3;

int main()
{
    scanf("%d%d%d",&x1,&x2,&x3);
    for(int i=0;i<=2;i++)
    {
        for(int j=5;j>=0;j--)
        {
            scanf("%d",&cnt[i][j]);
            sum[i]+=cnt[i][j]*mz[j];
            tot+=cnt[i][j]*mz[j];
            msum[j]+=cnt[i][j];
        }
    }
    memset(dp[0],-1,sizeof(dp[0]));
    dp[0][sum[0]][sum[1]]=0;
    for(int i=0;i<=5;i++)
    {
        int now=1&i;
        memset(dp[now^1],-1,sizeof(dp[now^1]));
        for(int j=0;j<=tot;j++)
        {
            for(int k=0;k+j<=tot;k++)
            {
                if(dp[now][j][k]>=0)
                {
                    for(int a=0;a<=msum[i];a++)
                    {
                        for(int b=0;a+b<=msum[i];b++)
                        {
                            int suma=j+(a-cnt[0][i])*mz[i];
                            int sumb=k+(b-cnt[1][i])*mz[i];
                            if(suma>=0&&sumb>=0&&suma+sumb<=tot)
                            {
                                int cnt1=(abs(a-cnt[0][i])+abs(b-cnt[1][i])+abs(msum[i]-cnt[2][i]-a-b))/2;
                                if(dp[now^1][suma][sumb]==-1||dp[now^1][suma][sumb]>dp[now][j][k]+cnt1) dp[now^1][suma][sumb]=dp[now][j][k]+cnt1;   
                            }
                        }
                    }
                }
            }
        }
    }
    int lasta=sum[0]-x1+x3,lastb=sum[1]-x2+x1,lastc=sum[2]-x3+x2;
    if(lasta<0||lastb<0||lastc<0||dp[6&1][lasta][lastb]<0) printf("impossible\n");
    else printf("%d\n",dp[6&1][lasta][lastb]);
    return 0;   
}

猜你喜欢

转载自blog.csdn.net/qq_39662197/article/details/80176741