lightoj 1074 spfa判断负环 dfs标记

要求:n个带权值的点,m条有向边,边的权值为(终点的权值-起点的权值)的三次方,问从第1个点到第k个点的最短距离,若小于3,则输出?,否则输出权值。

方法:spfa判断负环 dfs标记

1.这道题乍一看是单源最短路,直接上dijkstra,但是这个题存在负环,因此不能用dijkstra。因为循环负环可以使dis数组不断减小。

2.spfa可以判断负环。spfa的原理是先将源点入队,然后队首元素出队,不断用队首元素更新dis数组,可以被更新的结点继续入队。不断重复入队出队操作。但需要注意若某个结点入队超过n-1次即不能入队,这个点在负环中。用dfs的方法将这个结点可以到达的所有结点标记,认为这些点的dis可以无限小。

3.注意判断语句是num[i] >= n !!!不是== !!!因为可能多个点添加了这个点的次数使这个点的次数超过了n

4.spfa可以更新最短路径,也可以判断负环,按照题意输出即可。

5.这道题用邻接链表存边更节省时间。

#include<iostream>
#include<stdio.h>
#include<queue>
#include<map>
#include<string.h>
#include<math.h>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std ;
int n , m , q ;
int a[205] ;
int num[205] ;
bool vis[205] ;
int dis[205] ;
int map1[205][205] ;
struct edge
{
	int next[205] ;
	int cnt ;
}edge1[205] ;
int cal(int x , int y)
{
	return (a[y] - a[x]) * (a[y] - a[x]) * (a[y] - a[x]) ;
}
void dfs(int u)
{
	int i , j , k ;
	for(i = 0 ; i < edge1[u].cnt ; i ++)
	    if(map1[u][edge1[u].next[i]] != inf && !vis[edge1[u].next[i]])
	    {
	    	vis[edge1[u].next[i]] = 1 ;
	    	dfs(edge1[u].next[i]) ;
		}
}
void spfa(int cnt)
{
	int i , j , k ;
	int b , c ;
	int x ;
	queue <int> q1 ;
	num[1] ++ ;
	q1.push(1) ;
	dis[1] = 0 ;
	while(!q1.empty())
	{
		b = q1.front() ;
		q1.pop() ;
		if(vis[b])
		   continue ;
		for(i = 0 ; i < edge1[b].cnt ; i ++)
		    if(!vis[edge1[b].next[i]] && 
			   dis[b] + map1[b][edge1[b].next[i]] < dis[edge1[b].next[i]])
		    {
		    	dis[edge1[b].next[i]] = dis[b] + map1[b][edge1[b].next[i]] ;
		    	num[edge1[b].next[i]] ++ ;
		    	if(num[edge1[b].next[i]] >= n)
		    	{
		    	   vis[edge1[b].next[i]] = 1 ;
				   dfs(edge1[b].next[i]) ;	
				}
				else
				  q1.push(edge1[b].next[i]) ;
			}
	}
	printf("Case %d:\n" , cnt) ;
	for(i = 1 ; i <= q ; i ++)
	{
		scanf("%d" , &x) ;
		if(vis[x] || dis[x] < 3 || dis[x] == inf)
		   printf("?\n") ;
		else
		   printf("%d\n" , dis[x]) ;
	}
}
int main()
{
	int i , j , k ;
	int t , x , y ;
	scanf("%d" , &t) ;
	for(j = 1 ; j <= t ; j ++)
	{ 
      for(i = 1 ; i <= 200 ; i ++)
          edge1[i].cnt = 0 ;
	  memset(map1 , inf , sizeof(map1)) ;
	  memset(vis , 0 , sizeof(vis)) ;
	  memset(dis , inf , sizeof(dis)) ;
	  memset(num , 0 , sizeof(num)) ;
	  scanf("%d" , &n) ;
	  for(i = 1 ; i <= n ; i ++)
	    scanf("%d" , &a[i]) ;
	  scanf("%d" , &m) ;
	  for(i = 1 ; i <= m ; i ++)
	  {
	  	scanf("%d%d" , &x , &y) ;
	  	edge1[x].next[edge1[x].cnt ++] = y ;
	  	map1[x][y] = cal(x , y) ;
	  }
	  scanf("%d" , &q) ;
      spfa(j) ;	
	}
}

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/86646224