POJ 3634 circle of debt

POJ-3634

题意:有三个人,a欠b,b欠c,c欠a,同时他们自己身上有钱,1、5、10到100,若干个(会告诉你有多少个)。问最少多少次交换可以还清债务。

题解:

最终态是互相债务欠的一样多,这样子就不需要还了。

解决思路:

首先对于每个人都处理一遍dfs找用了多少钱还。不用想也一定是爆炸的。

考虑优化。

本质优化:从硬币的种类进行搜索。考虑每个硬币谁用了。

优化一:

假设:考虑3这个点:

1给了2a元,2给了3b元。

a<b:相当于2给了3 b-a元,1给了3 a元。

a>b:相当于1给了2 a-b元,1给了3 b元。

减少了由b的中转,方便处理的同时:1和3之间的关系也同时处理了。

优化二:

最优性剪枝:大于等于当前最佳答案的可以直接return。不加等于你也T(QAQ)

可行性剪枝:为了达到最终态,欠款差值的弥补=x*100+y*50+...易知:当前剩余金币种类的最大公因数提出来,如果能还清,肯定取余为0,所以用这个判断即可。

#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#include<cctype>
#include<string>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define LL long long
using namespace std;
/*唯有全力以赴*/

const int inf   = 1000000000;
int mon[7]={0,100,50,20,10,5,1};
int M[10][10],debt[10],ans,cnt;

bool check(int x,int money)
{
    switch(x)
    {
        case 1:return money % 100 == 0;
        case 2:return money % 50 == 0;
        case 3:case 4:return money % 10 == 0;
        case 5:return money % 5 == 0;
        case 6:return true;
    }
}

void dfs(int x)
{
    if(x==0){if(debt[1]==debt[2]&&debt[2]==debt[3])ans=min(ans,cnt);return;}
    if(cnt>=ans)return;
    if(!check(x,debt[1]-debt[2])||!check(x,debt[2]-debt[3]))return;
    //cout<<x<<endl;
    for(int k=1;k<=3;k++)
    {
        k--;int kn=(k+1)%3+1,knn=(k+2)%3+1;k++;
        for(int i=0;i<=M[k][x];i++)
        for(int j=0;j<=M[k][x]-i;j++)
        {
            ///cout<<"&"<<" "; 
            debt[k]-=i*mon[x];debt[knn]+=j*mon[x];cnt+=i+j;
            dfs(x-1);
            debt[k]+=i*mon[x];debt[knn]-=j*mon[x];cnt-=i+j;
        
        }
        for(int i=0;i<=M[kn][x];i++)
        for(int j=0;j<=M[knn][x];j++)
        {
            //cout<<"*"<<" ";
            debt[k]+=i*mon[x],debt[knn]-=j*mon[x];cnt+=i+j;
            dfs(x-1);
            debt[k]-=i*mon[x],debt[knn]+=j*mon[x];cnt-=i+j;
        }
    }
}

void init()
{
    FOR(i,1,3)scanf("%d",&debt[i]);
    FOR(i,1,3)FOR(j,1,6)scanf("%d",&M[i][j]);
    ans=inf;cnt=0;
    dfs(6);
    if(ans==inf)puts("impossible");
    else cout<<ans<<endl;
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        init();
    }
    system("pause");
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值