题源:https://nanti.jisuanke.com/t/11215
分析:这题是一个比较经典的网络流模型。把中间节点当做源,两端节点当做汇,对节点进行拆点,做一个流量为 22 的流即可。
吐槽:这是官方题解,然后其实赛场上谢了这个解法,但是我写搓了,因为最后输出路径的时候傻逼了
我居然向最短路一样记录前驱输出路径,简直傻逼了
着重强调(对我自己):网络流的一次增广是需要记录前驱的,但是多次增广以后,可以反向流,前驱就不对了
所以要判断哪些边,在最大流上时,需要枚举边,看哪个满流,然后随便搞搞
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <string> #include <stack> #include <vector> #include <map> #include <queue> #include <algorithm> #include <utility> using namespace std; typedef long long LL; const int N=505; const int INF=0x3f3f3f3f; int cap[N][N],flow[N][N],n,m,s,t; bool vis[N]; int p[N],a[N]; void maxflow(int s,int t){ queue<int>q; memset(flow,0,sizeof(flow)); while(1){ memset(a,0,sizeof(a)); memset(vis,false,sizeof(vis)); while(!q.empty())q.pop(); a[s]=INF;q.push(s); while(!q.empty()){ int u=q.front();q.pop(); if(u==t)break; for(int v=0;v<=n*2+1;++v) if(!a[v]&&flow[u][v]<cap[u][v]){ vis[v]=true; p[v]=u;a[v]=min(a[u],cap[u][v]-flow[u][v]); q.push(v); } } if(!a[t])break; for(int u=t;u!=s;u=p[u]){ flow[p[u]][u]+=a[t]; flow[u][p[u]]-=a[t]; } } } struct Edge{ int v,next; }edge[105*105]; int head[105],tot; void add(int u,int v){ edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++; } vector<int>ret; void dfs(int u,int f){ ret.push_back(u); for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].v; if(v==f)continue; dfs(v,u); } } int main() { int T; scanf("%d",&T); while(T--){ int a,b,mid; scanf("%d%d%d%d%d",&n,&m,&a,&b,&mid); s=0,t=n*2+1; memset(cap,0,sizeof(cap)); for(int i=0;i<m;++i){ int u,v; scanf("%d%d",&u,&v); cap[u+n][v]=cap[v+n][u]=1; } for(int i=1;i<=n;++i) cap[i][i+n]=1; cap[s][mid]=cap[mid][mid+n]=2; cap[a+n][t]=cap[b+n][t]=1; maxflow(s,t); memset(head,-1,sizeof(head));tot=0; for(int i=1+n;i<=n+n;++i){ if(!flow[i-n][i])continue; for(int j=1;j<=n;++j){ if(cap[i][j]&&flow[i][j]==cap[i][j]){ add(i-n,j);add(j,i-n); } } } ret.clear(); dfs(a,0); for(int i=0;i<ret.size();++i){ printf("%d",ret[i]); if(i+1==ret.size())printf(" "); else printf(" "); } } return 0; }