XYZZY

题目描述

最近有人发现了如何在Y-Crate游戏设备上运行开源软件。很多企业的设计师都已开发了冒险风格的游戏.并将其部暑到Y-Crate游戏设备上。编程任务:测试这些设计,看看哪些玩家会赢。
每场比赛最多有100个房间。其中一个房间是出发,一个房间是结束。 每个房间都有一个能量,大小为-100~100。 两个房间之间有一个单向的门廊。
在出发房间.玩家有100个能量值。她可以通过任何连接她所在的房间和另一个房间的门廊进人另一个房间。这个房间的能量值就增加到玩家的能量值中。这个过程继续下去.直到她进人结束房间赢了比赛,或能量耗尽而死亡(或沮丧退出)。在冒险的过程中,玩家可以多次进人同一个房间,每次都能获得它的能量值。

输入

输人包含多个测试例。每个测试例的第一行是n,即房间的数量。房间从1开始编号(出发房间).直到n(结束房间)。接下来是n个房间信息,每个房间一行或者多行。
(1)房间i的能量值。
(2)将要离开的房间i的门廊数量。
(3)通过这些门廊可以到达的房间列表。
出发和结束的房间.其能量值为0。最后一个测试例后面有一行,是-1.

输出

对每个测试例,输出一行:如果玩家可能会赢,则输出winnable. 否则输出hopeless.

样例输入
输入样例
样例输出
输出样例

算法分析

把房间当作结点,门廊当作边,本题就是计算最短路径问题。由于能量值在-100~100之间,即边的权重存在负值,所以不能采用单源最短路径算法,要采用最小费用流SPFA算法,它是Bellman-Ford算法的一种队列实现。
1. 数据结构
这里没有采用邻接矩阵,而是采用邻接表的结构。
const int MAX=105;
int n;//房间的数量
int energy;//每个房间的能量
vectorG[MAX];//图的邻接表
2. 采用最小费用流SPFA算法计算玩家是否会赢
求单源最短路的SPFA算法的全称是Shortest Path Faster Algorithm, 是西南交通大学段凡丁于1994年发表的。当给定的图存在负权边,这时类似Dijkstra 等算法便没有用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场。
用数组d记录每个结点的最短路径估计值,用邻接表存储图G。
采取的方法是动态逼近法:设立一个先进先出的队列q用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点指向的结点v进行松弛操作,如果τ点的最短路径估计值有所调整,且u点不在当前的队列中,就将u点放入队v。这样,不断从队列中取出结点进行松弛操作,直至队列空为止。如果加权有向图G不存在负权回路,即最短路径一定存在 ,则表示玩家赢得比赛。如果某个结点进人队列的次数超过n次,则存在负环(放弃该结点)。如果找不到最短路径,则表示玩家没有赢得比赛。

算法实现

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;

const int MAX=105;
int n;
int energy[MAX];
vector<int> G[MAX];

bool spfa()
{
	int num[MAX];
	int d[MAX];
	int vis[MAX];
	int i;
	for(i=1; i<=n; i++)
	{
		d[i] = 0;
		vis[i] = 0;
		num[i] = 0;
	}
	d[1] = 100;
	queue<int> q;
	q.push(1);
	vis[1] = 1;
	while(!q.empty())
	{
		int u = q.front();
		q.pop();
		if(num[u]==n+1) continue;
		num[u]++;
		vis[u] = 0;
		if(num[u]==n+1) d[u] = 1000000;
		for(i=0; i<G[u].size(); i++)
		{
			int v = G[u][i];
			if((d[v]<d[u]+energy[v])&&(d[u]+energy[v]>0))
			{
				if(v==n) return true;
				d[v] = d[u]+energy[v];
				if(!vis[v])
				{
					q.push(v);
					vis[v] = 1;
				}
			}
		}
	}
	return false;
}
int main()
{
	while(scanf("%d",&n)==1 && n!=-1)
	{
		int i, j;
		for(i=0; i<=n; i++)
			G[i].clear();
		for(i=1; i<=n; i++)
		{
			scanf("%d",energy+i);
			int m;
			int room;
			scanf("%d",&m);
			for(j=0; j<m; j++)
			{
				scanf("%d",&room);
				G[i].push_back(room);
			}
		}
		if(spfa())	puts("winnable");
		else  puts("hopeless");
	}
	return 0;
}

引用:《ACM大学生程序设计竞赛在线题库最新精选》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值