UVA 10603 了解搜索与图论的联系

思路借鉴自《算法竞赛入门经典》刘汝佳 著

题目概要:设3个杯子的容量分别为a,b,c,一开始只有第3个杯子装满了c升水,其余杯子为空。需求求解最少需要倒多少升水才能让某个杯子中的水有d升。如果无法做到d升,则让杯子的水达到d‘升(d'尽量接近d)

分析:一道搜索的题,这里需要把杯子中水的量视作一个状态(a,b,c),然后倒水可以视作一次状态的改变(状态结点的扩展)

设某一状态A(a1,b1,c1)到另一状态B(a2,b2,c2),需倒水amount升。

起始状态(0,0,c)

终止状态(x,y,z){x,y,z中存在一个等于d,或者所有状态搜索完成后,有一个最接近d的d’}

现在我们可以把两个相邻状态A,B视作顶点v,u,

状态A与状态B之间的转移需要倒amount升水,

即edge<v,u>的权重为amount,因此整个状态转移过程可以构成一个有向图。

因此我们需要求解的问题,从而转化为求出起始状态顶点到终止状态顶点的最短路径问题,

从而可以用dijkstra算法求解。

下面是具体代码:

#include<cstring>
#include<stdio.h>
#include<iostream>
#include<queue>
using namespace std;
#define MAX 205
int vis[MAX][MAX], cap[3], ans[MAX];
int a, b, c, d;
class state
{
public:
	int v[3], dist;
	state() {}
	bool operator <(const state& rhs) const
	{
		return this->dist > rhs.dist;
	}
};
int min(int a, int b)
{
	return a < b ? a : b;
}
void update_ans(const state& s)
{
	for (int i = 0;i != 3;++i)
	{
		int d = s.v[i];
		if (ans[d] < 0 || s.dist < ans[d])
		{
			ans[d] = s.dist;
		}
	}
}
void dijkstra()
{
	priority_queue<state, vector<state> > pq;
	state begin;
	begin.v[0] = 0;
	begin.v[1] = 0;
	begin.v[2] = c;
	begin.dist = 0;
	pq.push(begin);
	vis[0][0] = 1;
	while (!pq.empty())
	{
		state next = pq.top();
		pq.pop();
		update_ans(next);
		if (ans[d] > 0)
			break;
		for (int i = 0;i != 3;++i)
		{
			for (int j = 0;j != 3;++j)
			{
				if (i == j || next.v[i] == 0 || next.v[j] == cap[j])
					continue;
				/*倒水(改变状态)*/
				int amount = min(cap[j], next.v[i] + next.v[j]) - next.v[j];
				state s;
				memcpy(&s, &next, sizeof(next));
				s.dist = next.dist + amount;
				s.v[i] -= amount;
				s.v[j] += amount;
				if (!vis[s.v[0]][s.v[1]])//水量一定,确定2个量,就能确定整个状态
				{
					vis[s.v[0]][s.v[1]] = 1;
					pq.push(s);
				}
			}
		}
	}
	while (d >= 0)
	{
		if (ans[d] >= 0)
		{
			printf("%d %d\n", ans[d], d);
			return;
		}
		--d;
	}
}

int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		cin >> a >> b >> c >> d;
		cap[0] = a;
		cap[1] = b;
		cap[2] = c;
		memset(vis, 0, sizeof(vis));
		memset(ans, -1, sizeof(ans));
		dijkstra();
	}
	return 0;
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值