您的位置:首页 > 其它

Wormholes(虫洞) 判断负环 + 队列优化Bellman邻接表实现

2019-07-29 20:55 14 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_44855285/article/details/97673636

Wormholes 图中有无负环的判断

John在他的农场中闲逛时发现了许多虫洞。虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前)。John的每个农场有M条小路(无向边)连接着N (从1…N标号)块地,并有W个虫洞(有向边)。其中1<=N<=500,1<=M<=2500,1<=W<=200。 现在John想借助这些虫洞来回到过去(出发时刻之前),请你告诉他能办到吗。 John将向你提供F(1<=F<=5)个农场的地图。没有小路会耗费你超过10000秒的时间,当然也没有虫洞回帮你回到超过10000秒以前。

【输入格式】

  • Line 1: 一个整数 F, 表示农场个数。

  • Line 1 of each farm: 三个整数 N, M, W。

  • Lines 2…M+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条用时T秒的小路。

  • Lines M+2…M+W+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条可以使John到达T秒前的虫洞。

【输出格式】

  • Lines 1…F: 如果John能在这个农场实现他的目标,输出"YES",否则输出"NO"。

//翻译转载

一个图上有正权边和负权边,判断是否可以构成负权回路。
判断负权回路,那我们要用到Bellman算法了,之前没怎么用过邻接表存图,在这里放一个写的模板。

/*
核心:
原始:枚举每条边,用边收缩到源点的距离,进行n-1次枚举收缩操作(无负环则最多n-1次即可)
队列优化:用边去收缩每个点到源点的距离,边选择到源点的距离改变了的点的临边
一开始把源点放入队列,从队列中取点,用这些点的临边尝试收缩,再把成功收缩了的点放入队列
如果点已经在队列中,就跳过

Bellman和Dijkstra的不同:
B的book数组是用来标记点是否在队列中,若点弹出,book就要复0,D的book数组是用了标记是否用这个点为中转收缩过,**每个点只作为中转1次**。
B的队列是普通队列,D的为以到源点距离为判断标准的优先队列

Bellman时间复杂度为n*m,即使优化后,时间复杂度还是玄学,除了处理负权负环外,应使用Dijkstra。(若n*m不会超时间当然可以)
*/
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

#define Max 0x3f3f3f3f

int node[505],book[505],num[505],dis[505],cnt/*边容量*/;

struct E{
int b,c;
int next;
}e[6000];

void add(int a,int b,int c){
cnt++;
e[cnt].b = b;
e[cnt].c = c;
e[cnt].next = node[a];
node[a] = cnt;
}
void init(int n,int m){
cnt = 0;
memset(node,-1,sizeof(node));
memset(book,0,sizeof(book));
memset(num,0,sizeof(num));
memset(dis,Max,sizeof(dis));
/*
for(int i=1; i<=n; i++){
node[i] = -1;
book[i] = 0;
num[i] = 0;
dis[i] = Max;
}
*/
for(int i=1; i<=m; i++) e[i].next=-1;
}

void Bell(int n){ //n个点
queue<int>que;
int f=0; //默认无负环
que.push(1);
dis[1] = 0;     //判断负环,随便从1号点搜就可以了
book[1] = 1;
while(!que.empty()){
int x=que.front(); que.pop(); //取走就弹出 大佬告诉我的好习惯
book[x] = 0;
int k=node[x];
while(k!=-1){
int b=e[k].b, c=e[k].c;//x的邻点
if(dis[b]>dis[x]+c){
dis[b] = dis[x]+c;
num[b]++;
if(num[b]>=n){
f = 1;
break;
}
if(book[b]==0) que.push(b);
}
k = e[k].next;
}
if(f==1) break;//有环,无限收缩,退出
}
if(f==1) printf("YES\n");
else     printf("NO\n");
}

int main()
{
int F, n,m,w;
scanf("%d",&F);
for(int I=1; I<=F; I++){
scanf("%d%d%d",&n,&m,&w);
init(n , m*2+w);
for(int i=1; i<=m; i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
for(int i=1; i<=w; i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,-c);
}
Bell(n);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: