UVa10603 Fill

时间:2021-09-24 19:27:13

解题思路:这是神奇的一题,一定要好好体会。见代码:

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = ;
int vis[maxn][maxn], ans[maxn], cap[]; struct node{
int v[], cnt;
friend bool operator <(node A, node B){
return A.cnt > B.cnt; //cnt更小的优先级更高
}
}; priority_queue<node> q; //优先队列 void Change(node u)
{
for(int i = ; i < ; i++)
{
int d = u.v[i];
//ans[d] < 0表示该点目前没有访问过
//找出走到容量为d的点时所倒水的最小总量
if(ans[d] < || u.cnt < ans[d]) ans[d] = u.cnt;
}
return ;
} void solve(int a, int b, int c, int d)
{
cap[] = a, cap[] = b, cap[] = c; //初始化三个杯子的容量
node s, s1;
//每个杯子中初始状态所含有的水
s.v[] = , s.v[] = , s.v[] = c, s.cnt = ;
vis[][] = ; //标记第一个和第二个杯子中水的状态已经出现过
while(!q.empty()) q.pop();
q.push(s); while(!q.empty())
{
s = q.top();
q.pop(); Change(s); //好好体会
if(ans[d] >= ) break; //表示已经符合题意,可以跳出 //从杯子i中把水倒到杯子j中
for(int i = ; i < ; i++) for(int j = ; j < ; j++)
{
if(i == j) continue; //不可能倒水到自身
if(s.v[i] == || s.v[j] == cap[j]) continue;//i为空或j已经满
int tmp = min(s.v[i], cap[j] - s.v[j]); //到底倒完i还是将j倒满
s1 = s; //s的状态要保存下来,还要进行下一次循环。
//杯子i中的水增加,j中的水减少,总转移两增加
s1.v[i] = s.v[i] - tmp, s1.v[j] = s.v[j] + tmp, s1.cnt = s.cnt + tmp;
if(!vis[s1.v[]][s1.v[]]) //如果这种状态之前没出现过
{
vis[s1.v[]][s1.v[]] = ;
q.push(s1); //入队列
}
} }
while(d >= )
{
if(ans[d] >= ) //符合条件,刚好为题目所说的d,或与它接近
//的最小符合条件的数
{
printf("%d %d\n", ans[d], d);
return ; //直接返回
}
d --;
}
return ;
} int main()
{
int t, a, b, c, d;
scanf("%d", &t);
while(t --)
{
scanf("%d %d %d %d", &a, &b, &c, &d); memset(vis, , sizeof(vis));//一定要初始化
memset(ans, -, sizeof(ans)); solve(a, b, c, d);
}
return ;
}