uva 10557 - XYZZY 关键词:正环,无敌bfs,单源最短路spfa算法

笔者用时:4h。
方法一:当发现有个和为正值的环存在时,直接求看该点能否直接到达终点。如果可以,直接返回true,即winnable。否则,不用做任何的标记。因为不会再去探索它是否可以直接到达终点。这个方法效率很高。rank:7。在搜索的过程中看看有没有正环,没有正环则继续搜索,当发现所有路径都不能到达终点时,返回假值。

方法二:要低效一点。rank:300左右。用的spfa。

用最后一组给的样例,能量值改变情况如下表。通过不断地进行松弛操作,使得存在回路的点对应的能量值不断变大变大,直到可以过五关斩六将,到达终点能量依然大于0为止。或者是能量不断变大的次数超过一个给定的值(这个值要很大很大,评测数据说了算),这时认为可能因为根本就到达不了终点,或加能量、减能量疲劳而死,返回假值。


一:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
const int maxn=100;

int V[maxn],n;
bool vis[maxn],G[maxn][maxn];
int q[maxn],E[maxn];

bool bfs(int u)
{
	int front,rear;
	front=rear=0;
	memset(vis,0,sizeof(vis));
	vis[u]=true;
	q[rear++]=u;
	while(front<rear)
	{
		int u=q[front++];
		for(int i=0;i<n;i++) if(G[u][i] && !vis[i])
		{
			if(i==n-1) return true;
			q[rear++]=i;
			vis[i]=true;
		}
	}
	return false;
}

bool dfs(int u,int e)
{
	if(u==n-1) return true;
	for(int i=0;i<n;i++) 
	  if(G[u][i] && e+V[i]>0)
	 	{
	 		if(!E[i])
	 		{
	 			E[i]=e+V[i];
	 			if(dfs(i,E[i])) return true;
	 		}
	 		else if(e+V[i]>E[i] && bfs(i))  return true;
 		}
	return false;
}
	
int main()
{
#ifndef ONLINE_JUDGE
    freopen("10557.txt","r",stdin);
#endif
	while(scanf("%d",&n)==1 && n!=-1)
	{
		memset(G,0,sizeof(G));
		int i,j,k;
		for(i=0;i<n;i++)
		{
			scanf("%d%d",&V[i],&k);
			for(j=0;j<k;j++)
			{
				int t;
				scanf("%d",&t);
				G[i][t-1]=true;
			}
		}
		memset(E,0,sizeof(E));
		printf("%s\n",dfs(0,100)?"winnable":"hopeless");
	}
    return 0;
}


方法二:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define INF 500000
const int maxn=100;

int V[maxn],n,q[maxn],E[maxn];
bool vis[maxn],G[maxn][maxn];

bool spfa()
{
	int front,rear;
	front=rear=0;
	memset(vis,0,sizeof(vis));
	vis[0]=true;
	E[0]=100;
	q[rear++]=0;
	int cnt=0;
	while(front!=rear)
	{
		int u=q[front];
		++front%=maxn;
		//printf("u=%d\n",u);
		for(int i=0;i<n;i++) 
		  if(G[u][i] && E[i]<E[u]+V[i])
		  {
			 E[i]=E[u]+V[i];
 	                 if(!vis[i])
		 	 {
	 			q[rear]=i;
		 		++rear%=maxn;
		 		vis[i]=true;
		 		cnt++;
		 	 }
		  }
  		if(E[n-1]>0) return true;
  		else if(cnt>INF) return false;
  		vis[u]=false;
	}
	return false;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("10557.txt","r",stdin);
#endif
	while(scanf("%d",&n)==1 && n!=-1)
	{
		memset(G,0,sizeof(G));
		int i,j,k;
		for(i=0;i<n;i++)
		{
			scanf("%d%d",&V[i],&k);
			for(j=0;j<k;j++)
			{
				int t;
				scanf("%d",&t);
				G[i][t-1]=true;
			}
		}
		memset(E,0,sizeof(E));
		printf("%s\n",spfa()?"winnable":"hopeless");
	}
    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值