递归型SPFA+二分答案 || 负环 || BZOJ 4773
题解:
基本思路是二分答案,每次用Dfs型SPFA验证该答案是否合法。
一点细节我注释在代码里了。
代码:
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 inline int rd(){ 5 int x=0,f=1; char c=getchar(); 6 while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} 7 while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} 8 return f*x; 9 } 10 const int maxn=305,maxm=305*305,inf=(1<<30)-5; 11 int N,M,edge_head[maxn],num_edge=0,u,v,w,Dis[maxn]; 12 bool vis[maxn],flag; 13 struct Edge{ int to,nx,dis; }edge[maxm]; 14 inline void Add_edge(int from,int to,int dis){ 15 edge[++num_edge].nx=edge_head[from]; 16 edge[num_edge].to=to; 17 edge[num_edge].dis=dis; 18 edge_head[from]=num_edge; 19 return; 20 } 21 inline void SPFA(int x,int now,int Limit){ 22 //判断是否存在当前位于x,现在已经用了now个点,点数不超过Limit的负环 23 if(flag) return; 24 for(int i=edge_head[x];i;i=edge[i].nx){ 25 int y=edge[i].to; 26 if(Dis[y]>=Dis[x]+edge[i].dis){ 27 if(vis[y]){ 28 flag=1; 29 return; 30 } 31 else if(now+1<=Limit){ 32 vis[y]=1; 33 Dis[y]=Dis[x]+edge[i].dis; 34 SPFA(y,now+1,Limit); 35 vis[y]=0; 36 //这里的Dis[y]不用回溯,其实是一种剪枝 37 } 38 } 39 } 40 return; 41 } 42 int main(){ 43 N=rd(); M=rd(); 44 for(int i=1;i<=M;i++){ 45 u=rd(); v=rd(); w=rd(); 46 Add_edge(u,v,w); 47 } 48 49 flag=0; 50 for(int i=1;i<=N;i++){ 51 memset(vis,0,sizeof(vis)); 52 memset(Dis,0,sizeof(Dis)); 53 vis[i]=1; 54 SPFA(i,1,N); 55 if(flag) break; 56 } 57 if(flag==0){ 58 printf("0\n"); 59 return 0; 60 } 61 62 int l=2,r=N; 63 while(l<=r){ 64 int mid=(l+r)>>1; 65 flag=0; 66 for(int i=1;i<=N;i++){ 67 memset(vis,0,sizeof(vis)); 68 memset(Dis,0,sizeof(Dis)); 69 vis[i]=1; 70 SPFA(i,1,mid); 71 if(flag){ 72 r=mid-1; 73 break; 74 } 75 } 76 if(!flag) l=mid+1; 77 } 78 printf("%d\n",l); 79 return 0; 80 }
By:AlenaNuna