题目:https://www.luogu.org/problemnew/show/P1993
如果a==b,则等价于a-b>=0,b-a>=0
本题给出三种解法。
一是bfs_spfa+单端队列;二是dfs_spfa;三是bfs_spfa+双端队列
解法一:bfs_spfa+单端队列。4106ms,耗时较长。第6、10个点都超过1000ms
判断正环,有两个方法:
一)最长链长度超过n
二)某个点松驰次数超过n
解法二:dfs_spfa。48ms
通过递归深度可以很方便地判断有没有正环。
关键代码如下:
void dfs_spfa(int from){
if(flg)return;
in_stack[from]=true;//染色
for(int i=head[from];i;i=edge[i].nxt){
int to=edge[i].to;
int w=edge[i].w;
if(dis[to]<dis[from]+w){//取大
dis[to]=dis[from]+w;
if(in_stack[to]) {
flg=1;//已在染色栈中,说明有正环
return;
}
else dfs_spfa(to);
}
}
in_stack[from]=false;//回溯
}
解法三:bfs_spfa+双端队列。1503ms
利用STL自带的双端队列deque,函数如下:
push_front(x):双端队列头部增加一个元素X
push_back(x):双端队列尾部增加一个元素x
pop_front():删除双端队列中最前一个元素
pop_back():删除双端队列中最后一个元素
front():返回首元素的引用
back():返回尾元素的引用
empty():若空,则返回true;否则返回true
因为是求最长路,所以将dis大的点压入队列尾部,小的压入队列头部。
关键代码如下:
AC代码一:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int Maxn=1e4+5;
int n,m;
int num,head[Maxn];
int hd=0,tl=1,que[4*Maxn],vis[Maxn];//que,非循环队列时,如果开2000*Maxn,才出正确答案
int dis[Maxn],tot[Maxn];
struct Edge{
int to,nxt,w;
}edge[4*Maxn];
void join(int from,int to,int w){
edge[++num].nxt=head[from];
edge[num].to=to;
edge[num].w=w;
head[from]=num;
}
bool spfa(int st){
for(int i=0;i<=n;i++)dis[i]=-1e9;
hd=0;
tl=1;
que[1]=st;
dis[st]=0;
tot[st]=1;
vis[st]=1;
while(hd!=tl){
hd=hd%20007+1;
int from=que[hd];
vis[from]=0;
for(int i=head[from];i;i=edge[i].nxt){
int to=edge[i].to;
int w=edge[i].w;
if(dis[to]<dis[from]+w){//取大
dis[to]=dis[from]+w;
tot[to]=tot[from]+1;
//cout<<tot[to]<<' ';
if(tot[to]>n)return 1;//判断有没有正权回路
if(!vis[to]){
tl=tl%20007+1;
que[tl]=to;
vis[to]=1;
}
}
}
}
return 0;
}
int main(){
//freopen("testdata3.in","r",stdin);
cin>>n>>m;
int id,a,b,c;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&id,&a,&b);
if(id==1){
scanf("%d",&c);
join(b,a,c);//ai-bi>=ci
}
if(id==2){
scanf("%d",&c);
join(a,b,-c);//ai-bi<=ci,bi-ai>=-ci
}
if(id==3){
join(a,b,0);
join(b,a,0);
}
}
for(int i=1;i<=n;i++)
join(0,i,0);//虚拟点0
if(spfa(0)){
printf("%s","No");
}
else printf("%s","Yes");
return 0;
}
AC代码二:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int Maxn=1e4+5;
int n,m;
int num,head[Maxn];
bool in_stack[10*Maxn],flg;
int dis[Maxn];
struct Edge{
int to,nxt,w;
}edge[10*Maxn];
void join(int from,int to,int w){
edge[++num].nxt=head[from];
edge[num].to=to;
edge[num].w=w;
head[from]=num;
}
void dfs_spfa(int from){
if(flg)return;
in_stack[from]=true;//染色
for(int i=head[from];i;i=edge[i].nxt){
int to=edge[i].to;
int w=edge[i].w;
if(dis[to]<dis[from]+w){//取大
dis[to]=dis[from]+w;
if(in_stack[to]) {
flg=1;//已在栈中,说明有正环
return;
}
else dfs_spfa(to);
}
}
in_stack[from]=false;//回溯
}
int main(){
cin>>n>>m;
int id,a,b,c;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&id,&a,&b);
if(id==1){
scanf("%d",&c);
join(b,a,c);//ai-bi>=ci
}
if(id==2){
scanf("%d",&c);
join(a,b,-c);//ai-bi<=ci,bi-ai>=-ci
}
if(id==3){
join(a,b,0);
join(b,a,0);
}
}
for(int i=1;i<=n;i++)
join(0,i,0);//虚拟点0
for(int i=0;i<=n;i++)dis[i]=-1e9;
dis[0]=0;
dfs_spfa(0);
if(flg){
printf("%s","No");
}
else printf("%s","Yes");
return 0;
}
AC代码三:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<deque>
using namespace std;
const int Maxn=1e4+5;
int n,m;
int num,head[Maxn];
int vis[Maxn],dis[Maxn],tot[Maxn];
deque<int> q;
struct Edge{
int to,nxt,w;
}edge[4*Maxn];
void join(int from,int to,int w){
edge[++num].nxt=head[from];
edge[num].to=to;
edge[num].w=w;
head[from]=num;
}
bool spfa(int st){
for(int i=0;i<=n;i++)dis[i]=-1e9;
q.push_front(st);
dis[st]=0;
tot[st]=1;
vis[st]=1;
while(!q.empty()){
int from=q.front();
q.pop_front();
vis[from]=0;
for(int i=head[from];i;i=edge[i].nxt){
int to=edge[i].to;
int w=edge[i].w;
if(dis[to]<dis[from]+w){//取大
dis[to]=dis[from]+w;
tot[to]=tot[from]+1;
if(tot[to]>n)return 1;//判断有没有正权回路
if(!vis[to]){
if(!q.empty() && dis[to]>dis[q.front()])
q.push_back(to);
else q.push_front(to);
vis[to]=1;
}
}
}
}
return 0;
}
int main(){
cin>>n>>m;
int id,a,b,c;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&id,&a,&b);
if(id==1){
scanf("%d",&c);
join(b,a,c);//ai-bi>=ci
}
if(id==2){
scanf("%d",&c);
join(a,b,-c);//ai-bi<=ci,bi-ai>=-ci
}
if(id==3){
join(a,b,0);
join(b,a,0);
}
}
for(int i=1;i<=n;i++)
join(0,i,0);//虚拟点0
if(spfa(0)){
printf("%s","No");
}
else printf("%s","Yes");
return 0;
}