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");
}