POJ1135 多米诺效应(单源最短路径+枚举)

有N个关键的多米诺骨牌,这些牌通过一些路径相连接,这些路径是由一排其他骨牌构成的。已知每一条路径上的骨牌倒下需要的时间。现在从把编号为1的关键骨牌推倒,问多长时间后所有的骨牌(包括关键牌和它们之间的路径上的骨牌)都倒下,时间精确到小数点后一位。

骨牌可以往两个方向倒,因此路径都是双向的。可以把N个关键牌抽象为N个点,路径抽象为点之间的边。然后通过求单源最短路径的算法求出从第一个关键牌到其他关键牌所需的最短时间。要求的是所有牌倒下的时间,而关键牌可以看成路径的起点或终点,因此可以枚举每一条边,根据边的两个端点的最短路径以及边长,确定一下当前边的所有牌倒下的时间是多少。取这些边中倒下的时间最长的那一个作为答案,同时记录最后一张倒下的牌是端点还是中间的牌,如果是端点,则记录它是第几个关键牌,如果不是端点,则记录这条边的起始关键牌和结束关键牌的序号。如果只有一个关键点,那么倒下的时间是0。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 501;
const int E = 250001;
const int MAX = 0xfffffff;

struct Edge
{
	int pnt;
	int dis;
	int next;
}edge[E];
int cur;
int neigh[N];
int n, e;
int mindis[N];
int que[N], front, rear;
bool inque[N];
double ans;
int ansbeg, ansend;

void init()
{
	cur = 0;
	for (int i = 0; i < n; ++i) neigh[i] = -1;
}

void addedge(int beg, int end, int dis)
{
	edge[cur].pnt = end;
	edge[cur].dis = dis;
	edge[cur].next = neigh[beg];
	neigh[beg] = cur;
	++cur;
}

void spfa(int s)
{
	for (int i = 0; i < n; ++i) 
	{
		mindis[i] = MAX;
		inque[i] = false;
	}
	front = rear = 0;
	mindis[s] = 0;
	inque[s] = true;
	que[rear++] = s;
	while (front != rear)
	{
		int pre = que[front];
		inque[pre] = false;
		int te = neigh[pre];
		while (te != -1)
		{
			int pnt = edge[te].pnt;
			if (mindis[pre] + edge[te].dis < mindis[pnt])
			{
				mindis[pnt] = mindis[pre] + edge[te].dis;
				if (!inque[pnt])
				{
					inque[pnt] = true;
					que[rear++] = pnt;
					if (rear == N) rear = 0;
				}
			}
			te = edge[te].next;
		}
		if (++front == N) front = 0;
	}
}

void updateans(double tans, int tbeg, int tend)
{
	if (tans > ans)
	{
		ans = tans;
		ansbeg = tbeg;
		ansend = tend;
	}
}

void getans()
{
	double t;
	int te, pnt;
	ans = -MAX;
	for (int i = 0; i < n; ++i)
	{
		te = neigh[i];
		while (te != -1)
		{
			pnt = edge[te].pnt;
			if (mindis[i] + edge[te].dis == mindis[pnt])
			{
				t = (mindis[i] + mindis[pnt] + edge[te].dis) / 2.0;
				updateans(t, pnt, pnt);
			}
			else if (mindis[i] + edge[te].dis > mindis[pnt])
			{
				t = (mindis[i] + mindis[pnt] + edge[te].dis) / 2.0;
				updateans(t, i, pnt);
			}
			te = edge[te].next;
		}
	}
	if (ans == -MAX) 
	{
		ans = 0;
		ansbeg = ansend = 0;
	}
	if (ansbeg > ansend) swap(ansbeg, ansend);
}

int main()
{
	int beg, end, dis, T = 1;
	while (scanf("%d%d", &n, &e) != EOF)
	{
		if (n == 0 && e == 0) break;
		init();
		for (int i = 0; i < e; ++i)
		{
			scanf("%d%d%d", &beg, &end, &dis);
			--beg;
			--end;
			addedge(beg, end, dis);
			addedge(end, beg, dis);
		}
		spfa(0);
		getans();
		printf("System #%d\n", T++);
		if (ansbeg == ansend)
		{
			printf("The last domino falls after %.1lf seconds, at key domino %d.\n", ans, ansbeg + 1);
		}
		else
		{
			printf("The last domino falls after %.1lf seconds, between key dominoes %d and %d.\n", ans, ansbeg + 1, ansend + 1);
		}
		printf("\n");
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值