poj1679

题目不难,但是综合性比较高,也比较复杂。大致题意:

给定n个顶点,m条边,每条边的描述如下:uv  fuv  L fvu,即两点标号分别为u和v,从u到v边有权重fuv,从v到u边有权重fvu,从u到v和从v到u边的长度均为L。并给定起点和终点,要求出从起点到终点的路径中rewarding且路径权重之和最小并且在此基础上长度之和最小的路径。

这里对于题目的理解很重要:

1)rewarding路径定义:假设路径为1——2——3,则该路径为rewarding路径当且仅当该路径上各顶点的出边的权重是所有该顶点出边中最小的(或与最小相同,其实也是最小的),这里即1——2是所有1出发中的边中权重最小的边。同理:2——3也一样。若从起点到终点任意路径均不满足rewarding则输出“VOID“。

2)题目要求若存在rewarding情况,则下一步要判断的就是是否存在从起点到终点的权值无下限的路径。要想权值无下限也即从起点到终点存在负权环的情况。也就是要判断从起点到终点之间是否存在负权环。若存在则输出”NOBOUND“,否则输出最小的权值之和,和在此条件下的最小的长度之和。

分析到这里:我们遇到了三个问题:

1)如何解决判断是否存在rewarding路径问题?

解决方法为:根据rewarding路径的定义,路径中的每个点(除开终点)对应边要为从该点出发的所有边中权重最小的,要达到这个目的,我们可以只保留原始路径中满足此条件的边——从该点出发中权值最小的边。这样以后,就可以保证每条边都满足条件了。同时,存在这样的情况:存在多余1条从a到b的边,那么我们可以只保留其中一条边,首先根据权重判定,保留权重小的边,其次根据长度判定,保留长度较小的边。这样去除边以后对应图形就为每两个顶点之间最多只有一条边,且满足rewarding条件。注意可能存在自环的情况,但可以统一处理。

上述预处理后,接着就是判断从起点到终点在转化后的图形中是否存在一条可行路径。若从起点出发可以找到终点则说明存在rewarding路径。否则说明不存在即可输出”VOID“

判断方法即为深搜或广搜

2)如何解决判断在起点和终点之间是否存在负权回路的问题?

注意是判断起点和终点之间是否存在负权回路的问题。不是判断整个图有负权回路问题,原因很明显:整个图有负权回路不代表从地点到终点存在负权回路。而我们常见的bellman_ford算法和spfa算法只能判定整个图是否存在负权回路,无法直接判定两点之间是否存在负权回路问题。这就要理解spfa算法和bellman_ford算法本质了,然后做相应变形。我们知道bellman_ford和spfa算法都是通过松弛操作来达到获取最短路径的目的。bellman_ford算法对所有边松弛一次即可得到长度为1的最短路径,依次类推,松弛n次即可获取长度为n的最段路径(注意这里长度为n是最短路径中边的个数为n),而由于在不存在负权回路的情况下,最短路径只可能最长为n-1(n为图中顶点个数),故最多需要松弛n-1次。但实际上bellman-ford算法存在许多多余的操作,因为只有上一次成功松弛的顶点才能对其领结点成功松弛。故spfa算法用队列保留每次成功松弛的顶点,这样就可以避免许多不必要的操作了。那么说到这里:到底如何判断两点之间是否存在负权回路呢?

方法即为在利用spfa算法求最短路时若发现存在负环的请况,则判断从该点出发是否可以到达终点,若能够到达终点则说明从起点到终点存在负权回路(注意原点为起点的spfa算法所求的最短路径过程中涉及的点一定可以从原点经过某些路径到达,故有上述推论),若不能则可以将此负权回路舍弃,继续spfa算法,若发现另一个负权回路,则同样从该点出发,判断是否存在到达终点的路径。 不断进行下去,这样就只可能有两种情况:

11)从负权回路的某个点出发可以到达终点,则说明从起点出发可以到达终点。即从起点到终点存在负权回路,输出:"NOBOUND”

12)所有负权回路上的点都不能到达终点,那么说明从起点出发可以到达终点并且不存在负权回路,则输出最小的权重和长度。

负权回路判断的方法为顶点的松弛次数>n(n为顶点个数)

3)如何在实现在保证最小权重的情况下,实现最小长度?

这个其实比较好想:就是在松弛的时候外加一个条件:val[i]==val[point]+node[point][i].value && le[i]>le[point]+node[point][i].len)

即若权重相同的时候,长度可以松弛那么就进行松弛。

另外需要注意的就是输入的处理要小心。

下面是代码:10144k+63MS

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define Inf 100000010
#define Max 1110
using namespace std;
struct Node{
	int len;
	int value;
}node[Max][Max];
bool exist[Max][Max];
int val[Max];
int le[Max];
int used[Max];
int max_int[Max];
bool flag[Max];
bool vis[Max];
bool trag[Max];
int n,m;
int s,t;
bool dfs(int index){
	if(index==t)
		return true;
	flag[index]=true;
	for(int i=1;i<=n;i++)
		if(!flag[i] && node[index][i].value!=Inf)
			if(dfs(i))
				return true;
	return false;
}
void spfa(){
	queue<int> Que;
	memset(trag,0,sizeof(trag));
	memset(vis,0,sizeof(vis));
	memset(used,0,sizeof(used));
	for(int i=1;i<=n;i++)
		val[i]=le[i]=Inf;
	val[s]=0;
	le[s]=0;
    vis[s]=true;
	used[s]++;
	Que.push(s);
	while(!Que.empty()){
		int point=Que.front();
		Que.pop();
		vis[point]=false;
		if(used[point]>n){
			memset(flag,0,sizeof(flag));
			if(dfs(point)){
				printf("UNBOUND\n");
				return ;
			}
			trag[point]=true;
		}
		for(int i=1;i<=n;i++)
			if(!trag[i] &&  node[point][i].value!=Inf){
				if(val[i]>val[point]+node[point][i].value || (val[i]==val[point]+node[point][i].value && le[i]>le[point]+node[point][i].len)){
					val[i]=val[point]+node[point][i].value;
					le[i]=le[point]+node[point][i].len;
					if(!vis[i]){
						vis[i]=true;
						Que.push(i);
						used[i]++;
					}
				}
				
			}
	}
    printf("%d %d\n",val[t],le[t]);
}

int main(){
	while(scanf("%d%d",&n,&m)!=EOF){
		for(int i=1;i<=n;i++){
			max_int[i]=Inf;
			for(int j=1;j<=n;j++)
				node[i][j].len=node[i][j].value=Inf;
		}
        scanf("%d%d",&s,&t);
		s++,t++;
		int u,v;
		memset(exist,0,sizeof(exist));
		for(int i=1;i<=m;i++){
			char temp=getchar();
			while(temp!='(')
				temp=getchar();
			scanf("%d",&u);
		    getchar();
			scanf("%d",&v);
			getchar();
			int num;
			scanf("%d",&num);
			u++,v++;
			int trag=1;
			if(exist[u][v] && node[u][v].value<num)
				trag=-1; 
			else if(exist[u][v] && node[u][v].value>num)
				trag=0;
			else if(exist[u][v] && node[u][v].value==num)
			    trag=2;
			if(num<max_int[u])
				max_int[u]=num;
			getchar();
			int length;
			scanf("%d",&length);
			if(trag==1){
				node[u][v].value=num;
				node[u][v].len=length;
				exist[u][v]=true;
			}
			else if(trag==0){
				node[u][v].value=num;
				node[u][v].len=length;
			}
			else if(trag==2){
			    if(node[u][v].len>length)
					node[u][v].len=length;
			}
			getchar();
			scanf("%d",&num);
			trag=1;
			if(exist[v][u] && node[v][u].value<num)
				trag=-1; 
			else if(exist[v][u] && node[v][u].value>num)
				trag=0;
			else if(exist[v][u] && node[v][u].value==num)
			    trag=2;
			if(num<max_int[v])
				max_int[v]=num;
			
			if(trag==1){
				node[v][u].value=num;
				node[v][u].len=length;
				exist[v][u]=true;
			}
			else if(trag==0){
				node[v][u].value=num;
				node[v][u].len=length;
			}
			else if(trag==2){
			    if(node[v][u].len>length)
					node[v][u].len=length;
			}
	
			getchar();
		}
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if(node[i][j].value>max_int[i])
					node[i][j].value=Inf;
		memset(flag,0,sizeof(flag));
		if(!dfs(s))
			printf("VOID\n");
		else
			spfa();
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值