要求: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) ;
}
}