POJ 2679 Adventurous Driving

SPFA算法

难点: 1. 起始点有自环, 不管正环或者负环, 都需要去判断是VOID还是UNBOUND;

         2. 判断负权环是否存在于路径上;(这个算法我一开始想把SPFA某个点进队列次数大于2*n或者更大的数把所有在负环中的点全部找出来,但是有可能做不到,比如从s开始分出2个叉A和B,t在A叉上,A叉上在距离s非常远处有个负环,而B叉上在距离s很近处就有个负环,这样A叉上的负环有可能检查不出来,所以我参考网上的一个算法,感觉没有漏洞,就是从t遍历逆图访问到的点做标记,然后在SPFA时不需要扩展未做标记的点,因为此点不可能到达t。如果有什么不周全的地方,还恳请路过的大牛赐教)

第二种想法代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#define inf 100000000
using namespace std;
int n,m,s,t;
int head[1200],cnt,head1[1200];
bool vis[1200],sig[1200],ok[1200];
int dis[1200],mon[1200],times[1200];
struct EDGE{
	int v,f,d,next;
}edge[22000];
void addedge(int u,int v,int f,int d){
	if(head[u]!=-1 && edge[head[u]].f<f)
		return;
	if(head[u]!=-1 && edge[head[u]].f>f)
		head[u]=-1;
	edge[cnt].v=v;
	edge[cnt].d=d;
	edge[cnt].f=f;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
int SPFA(int u){
	int i,j;
	memset(vis,0,sizeof(vis));
	memset(times,0,sizeof(times));
	for(i=0;i<n;i++){
		dis[i]=mon[i]=inf;
	}
	queue<int>que;
	que.push(u);
	vis[u]=1;
	dis[u]=mon[u]=0;
	times[u]++;
	while(!que.empty()){
		int tem=que.front();
		que.pop();
		vis[tem]=0;                   //把这句移到这里可以把负的自环判断出来
		if(times[tem]>n)           
			return 1;
		for(i=head[tem];i!=-1;i=edge[i].next){
			int v=edge[i].v;
			if(!ok[v])
				continue;
			if(mon[v]>mon[tem]+edge[i].f || (mon[v] == mon[tem]+edge[i].f && dis[v]>dis[tem]+edge[i].d)){
				mon[v]=mon[tem]+edge[i].f;
				dis[v]=dis[tem]+edge[i].d;
				if(!vis[v]){
					que.push(v);
					vis[v]=1;
					times[v]++;
				}
			}
		}
	}
	return 0;
}
void dfs(int a){
	int i,yes=0;
	ok[a]=1;
	for(i=head1[a];i!=-1;i=edge[i].next){
		if(!ok[edge[i].v])
			dfs(edge[i].v);
	}
}
int main(){
	int i,j;
	char str[1000];
	while(scanf("%d %d %d %d",&n,&m,&s,&t)==4){
		memset(head,-1,sizeof(head));
		cnt=0;
		for(i=1;i<=m;i++){
			int u,v,f1,l,f2;
			char tem;
			scanf("%s",str);
			sscanf(str,"(%d,%d,%d[%d]%d)",&u,&v,&f1,&l,&f2);
			addedge(u,v,f1,l);
			addedge(v,u,f2,l);
		}
		memset(ok,0,sizeof(ok));
		memset(head1,-1,sizeof(head1));
		for(i=0;i<n;i++){                                //建立反图
			for(j=head[i];j!=-1;j=edge[j].next){
				edge[cnt].v=i;
				edge[cnt].next=head1[edge[j].v];
				head1[edge[j].v]=cnt++;
			}
		}
		dfs(t);                                         //搜索所有能够到达t的点并标记
		int kk=SPFA(s);
		if(mon[t]==inf)
			printf("VOID\n");
		else if(kk==0)
			printf("%d %d\n",mon[t],dis[t]);
		else
			printf("UNBOUND\n");
	}
    return 0;
}


第一种想法代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#define inf 100000000
using namespace std;
int n,m,s,t;
int head[1200],cnt;
bool vis[1200],sig[1200];
int dis[1200],mon[1200],times[1200];
struct EDGE{
	int v,f,d,next;
}edge[11000];
void addedge(int u,int v,int f,int d){
	if(head[u]!=-1 && edge[head[u]].f<f)
		return;
	if(head[u]!=-1 && edge[head[u]].f>f)
		head[u]=-1;
	edge[cnt].v=v;
	edge[cnt].d=d;
	edge[cnt].f=f;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
int SPFA(int u){
	int i,j;
	memset(vis,0,sizeof(vis));
	memset(times,0,sizeof(times));
	for(i=0;i<=n;i++){
		dis[i]=mon[i]=inf;
	}
	queue<int>que;
	que.push(u);
	vis[u]=1;
	dis[u]=mon[u]=0;
	times[u]++;
	while(!que.empty()){
		int tem=que.front();
		que.pop();
		vis[tem]=0;                   //把这句移到这里可以把负的自环判断出来
		if(times[tem]>n*2)            //这里当一个点出现n*2次时,可以把所以环上的点标记好
			return 1;
		for(i=head[tem];i!=-1;i=edge[i].next){
			int v=edge[i].v;
			if(mon[v]>mon[tem]+edge[i].f || (mon[v] == mon[tem]+edge[i].f && dis[v]>dis[tem]+edge[i].d)){
				mon[v]=mon[tem]+edge[i].f;
				dis[v]=dis[tem]+edge[i].d;
				if(!vis[v]){
					que.push(v);
					vis[v]=1;
					times[v]++;
				}
			}
		}
	}
	return 0;
}
bool dfs(int a){
	int i;
	sig[a]=1;
	if(a==t)
		return 1;
	for(i=head[a];i!=-1;i=edge[i].next){
		if(!sig[edge[i].v]){
			dfs(edge[i].v);
		}
	}
	return 0;
}
int main(){
	int i,j;
	char str[1000];
	while(scanf("%d %d %d %d",&n,&m,&s,&t)==4){
		memset(head,-1,sizeof(head));
		cnt=0;
		for(i=1;i<=m;i++){
			int u,v,f1,l,f2;
			char tem;
			scanf("%s",str);
			sscanf(str,"(%d,%d,%d[%d]%d)",&u,&v,&f1,&l,&f2);
			addedge(u,v,f1,l);
			addedge(v,u,f2,l);
		}
		int kk=SPFA(s);
		if(mon[t]==inf)
			printf("VOID\n");
		else if(kk==0)
			printf("%d %d\n",mon[t],dis[t]);
		else{
			bool flag=0;
			memset(sig,0,sizeof(sig));
			for(i=0;i<n;i++){
				if(times[i]>n){
					if(!sig[i]){
						flag=dfs(i);
						if(sig[t])
							break;
					}
				}
			}
			if(sig[t])
				printf("UNBOUND\n");
			else
				printf("%d %d\n",mon[t],dis[t]);
		}
	}
    return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值