判断负环,也是一个小知识点了,之前一直比较迷,现在总结一下,再加一些板子,以后直接用了就。
判断负环,最常用的就是bellman-floy和spfa,暴力枚举/奇怪的贪心/超神搜索,这些算法了,但是因为要一般性,那些玄学算法就是放弃吧(难道真实原因不是非洲人没有玄学算法吗
Bellman-ford
两个神仙提出来的算法,据说一个好像还是动态规划的发明者(真神仙),
这个算法的思路就是,从源开始,初始化所有距离为INF||0...(视情况而定),然后将源置为0||val...
进行(n-1)*m次松弛判断,并且刷新结果。
之后再进行m次判断,如果仍存在一个点可被松弛,那么就说明这个存在负环。
代码如下:
#include<iostream>
using namespace std;
const int MAXN=1e5+10;
const int INF=0x3f3f3f3f;
struct node{
int u,v,val;
node(int _u=0,int _v=0,int _val=0){
u=_u;v=_v;val=_val;
}
}edge[MAXN];
int dis[MAXN];
int n,m;
int bellman_ford(){
memset(dis,INF,sizeof(dis));dis[1]=0;
for(int i=1;i<n;++i){
for(int j=1;j<=m;++j){
if(dis[edge[j].v]>dis[edge[j].u]+edge[j].val){
dis[edge[j].v]=dis[edge[j].u]+edge[j].val;
}
}
}int f=1;
for(int j=1;j<=m;++j){
if(dis[edge[j].v]>dis[edge[j].u]+edge[j].val){
f=0;break;
}
}return f;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;++i){
cin>>edge[i].u>>edge[i].v>>edge[i].val;
}
if(!bellman_ford()) cout<<"存在负环"<<endl;
else cout<<"不存在负环"<<endl;
}
SPFA
关于SPFA,它死了。
既然死了,就不需要再看了。
说笑说笑,SPFA哪怕日常被T,但是仍有着存在的价值的。下面写一个DFS_SPFA的板子,从1开始的负环判断(题面)
const int MAXN=3e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);
struct node{
int v,w,cost,nxt;
node(int _v=0,int _w=0,int _nxt=0):
v(_v),w(_w),nxt(_nxt){}
}edge[MAXN<<1];
int head[MAXN],ecnt;
int dis[MAXN],vis[MAXN];
int n,m,f;
void add(int u,int v,int w){
edge[ecnt]=node(v,w,head[u]);
head[u]=ecnt++;
}
void dfs_spfa(int u){
vis[u]=1;
for(int i=head[u];i+1;i=edge[i].nxt){
int v=edge[i].v;
if(dis[u]+edge[i].w<dis[v]){
dis[v]=dis[u]+edge[i].w;
if(vis[v]){
f=1;return ;
}
else dfs_spfa(v);
}
}vis[u]=0;
}
int main(){
int T;cin>>T;
while(T--){
clean(head,-1);clean(dis,INF32);clean(vis,0);
dis[1]=0;ecnt=0;
int n,m;cin>>n>>m;
int a,b,val;
for(int i=1;i<=m;++i){
cin>>a>>b>>val;
add(a,b,val);
if(val>=0) add(b,a,val);
}f=0;
dfs_spfa(1);
if(f) cout<<"YE5"<<endl;
else cout<<"N0"<<endl;
}
}
更新一个手写栈的实现代码,最近光用手写的栈了,感觉还是手写的比较舒服一点:
void dfs_spfa(){
clean(dis,INF32);dis[1]=0;
clean(vis,0);vis[1]=1;
clean(sum,0);sum[1]++;
st[tot++]=1;
while(tot){
int u=st[--tot];vis[u]=0;
for(int i=head[u];i+1;i=edge[i].nxt){
int v=edge[i].v;
if(dis[v]>dis[u]+edge[i].val){
dis[v]=dis[u]+edge[i].val;
if(vis[v]==0){
vis[v]=1;
st[tot++]=v;
sum[v]++;
if(sum[v]>=n){
flag=1;return ;
}
}
}
}
}
}
然后事BFA_SPFA的板子:
const int MAXN=3e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);
struct node{
int v,w,cost,nxt;
node(int _v=0,int _w=0,int _nxt=0):
v(_v),w(_w),nxt(_nxt){}
}edge[MAXN<<1];
int head[MAXN],ecnt;
int dis[MAXN],vis[MAXN],cnt[MAXN];
int n,m;
void intt(){
clean(head,-1);
clean(dis,INF32);
clean(vis,0);
clean(cnt,0);
ecnt=0;
}
void add(int u,int v,int w){
edge[ecnt]=node(v,w,head[u]);
head[u]=ecnt++;
}
int bfs_spfa(int u){
queue<int> que;que.push(u);
vis[u]=1;dis[u]=0;
while(que.size()){
int e=que.front();que.pop();
vis[e]=0;
if(cnt[e]>=n) return 1;
for(int i=head[e];i+1;i=edge[i].nxt){
int temp=edge[i].v;
if(dis[temp]>dis[e]+edge[i].w){
dis[temp]=dis[e]+edge[i].w;
if(vis[temp]==0){
que.push(temp);
vis[temp]=1;cnt[temp]++;
if(cnt[temp]>=n) return 1;
}
}
}
}return 0;
}
int main(){
int T;cin>>T;
while(T--){
intt();
cin>>n>>m;
int a,b,val;
for(int i=1;i<=m;++i){
cin>>a>>b>>val;
add(a,b,val);
if(val>=0) add(b,a,val);
}
if(bfs_spfa(1)) cout<<"YE5"<<endl;
else cout<<"N0"<<endl;
}
}
然后是根据这个两个板子写的两个题:
洛谷P3385:https://www.luogu.org/problemnew/show/P3385
这是一个单纯的板子题,用来检验板子的正确性的一道题。直接判断是否存在负环。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<map>
//#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// ??
//std::ios::sync_with_stdio(false);
const int MAXN=3e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);
struct node{
int u,v,val;
node(int _u=0,int _v=0,int _val=0){
u=_u;v=_v;val=_val;
}
}edge[MAXN];
int dis[MAXN];
int n,m,s,t,ecnt;
int bellman_ford(){
//clean(dis,INF32);dis[1]=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(dis[edge[j].v]>dis[edge[j].u]+edge[j].val){
dis[edge[j].v]=dis[edge[j].u]+edge[j].val;
}
if(edge[j].val>=0&&dis[edge[j].u]>dis[edge[j].v]+edge[j].val){
dis[edge[j].u]=dis[edge[j].v]+edge[j].val;
}
}
}int f=1;
for(int i=1;i<=m;++i){
if(dis[edge[i].v]>dis[edge[i].u]+edge[i].val){
f=0;break;
}
if(edge[i].val>=0&&dis[edge[i].u]>dis[edge[i].v]+edge[i].val){
f=0;break;
}
}return f;
}
int main(){
int T;
cin>>T;
while(T--){
ecnt=0;
cin>>n>>m;
int a,b,val;
for(int i=1;i<=m;++i){
cin>>edge[i].u>>edge[i].v>>edge[i].val;
}
if(!bellman_ford()) cout<<"YE5"<<endl;
else cout<<"N0"<<endl;
}
}
/*
*/
POJ-1860:http://poj.org/problem?id=1860
钱币兑换的发财之道
这是一个判断是否存在一个正权回路的,一样的思路:
AC:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define Pair pair<double,double>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
// register
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);
struct node{
int u,v;double cost,Comp;
node(int _u=0,int _v=0,double _cost=0,double _Comp=0){
u=_u;v=_v;
cost=_cost;Comp=_Comp;
}
}arr[MAXN];
double dis[MAXN];
int n,m,ecnt;
int bellman_ford(int s,double have){
dis[s]=have;
for(int i=1;i<n;++i){
for(int j=0;j<ecnt;++j){
if(dis[arr[j].v]<(dis[arr[j].u]-arr[j].Comp)*arr[j].cost){
dis[arr[j].v]=(dis[arr[j].u]-arr[j].Comp)*arr[j].cost;
}
}
}int f=1;
for(int i=0;i<ecnt;++i){
if(dis[arr[i].v]<(dis[arr[i].u]-arr[i].Comp)*arr[i].cost){
f=0;break;
}
}return f;
}
int main(){
int kind;double have;
cin>>n>>m>>kind>>have;
ecnt=0;
int a,b;double Rab,Cab,Rba,Cba;
for(int i=1;i<=m;++i){
cin>>a>>b>>Rab>>Cab>>Rba>>Cba;
arr[ecnt++]=node(a,b,Rab,Cab);
arr[ecnt++]=node(b,a,Rba,Cba);
}
if(!bellman_ford(kind,have)) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
然后是POJ-2240-Arbitrage
题目链接:http://poj.org/problem?id=2240
跟上一道题是一个意思,只不过这个换成字符串了,dfs-spfa判断走一波
AC:
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<map>
//#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// ??
//std::ios::sync_with_stdio(false);
const int MAXN=35;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);
double mp[MAXN][MAXN];
double dis[MAXN];
int vis[MAXN];
map<string,int> mp1;
int n,m,f;
void intt(){
for(int i=0;i<MAXN;++i){
for(int j=i;j<MAXN;++j){
mp[i][j]=mp[j][i]=0;
}dis[i]=-1;
}mp1.clear();clean(vis,0);
}
void dfs_spfa(int u){
vis[u]=1;
for(int i=1;i<=n;++i){
if(dis[i]<dis[u]*mp[u][i]){
dis[i]=dis[u]*mp[u][i];
if(vis[i]){
f=1;return ;
}
else{
dfs_spfa(i);
}
}
}vis[u]=0;
}
int main(){
int Case=1;
while(cin>>n){
if(n==0) break;
intt();
string kind;int k=1;
for(int i=1;i<=n;++i){
cin>>kind;
if(mp1[kind]==0) mp1[kind]=k++;
}cin>>m;
string kind2;double rate;
for(int i=1;i<=m;++i){
cin>>kind>>rate>>kind2;
if(mp[mp1[kind]][mp1[kind2]]<rate)//更大
mp[mp1[kind]][mp1[kind2]]=rate;
}
dis[1]=1;f=0;
dfs_spfa(1);
if(f) cout<<"Case "<<Case++<<": Yes"<<endl;
else cout<<"Case "<<Case++<<": No"<<endl;
}
}
/*
*/