菜鸟物流有自己的运输网络,网络中包含 nn 个城市物流集散中心,和m 对城市之间的运输线路(线路是双向的)。菜鸟物流允许淘宝卖家自行确定包裹的运输路径,但只有一条限制规则:不允许经过重复的城市。淘宝卖家小明从a 城市寄出快递后,希望包裹在 mid 城市进行包装加工以后再寄往 b 城市。
现在小明希望算出一个满足他需求的合法运输路径,你可以帮他算出来么?
已知这样的方案一定存在。请为小明输出任意一个可行方案。
输入格式
第一行一个正整数 T(1≤T≤10)T(1 \leq T \leq 10)T(1≤T≤10) 表示数据的组数。
每组数据第一行 2 个正整数n,m(3≤n≤100,m≤n(n−1)2)n,m(3 \leq n \leq 100,m \leq \frac{n(n-1)}{2})n,m(3≤n≤100,m≤2n(n−1)),表示城市个数和运输线路数目。
第二行 3 个互不相同正整数a,b,mid(1≤a,b,mid≤n)a,b,mid(1 \leq a,b,mid \leq n)a,b,mid(1≤a,b,mid≤n),表示起点、终点和途径城市。
接下来 m 行,每行2 个正整数x,y(1≤x,y≤n)x,y(1\leq x,y \leq n)x,y(1≤x,y≤n),表示每条线路连接的2 个城市。
每组数据一定存在至少一组合法方案。如果有多种满足小明需求的合法运输路径,输出任意一个即可。
输出格式
每组数据输出 L 个正整数,表示顺次经过的城市的编号,包括起点和终点。每两个整数之间一个空格,最后一个整数后面没有空格。
样例输入
1 5 5 1 5 3 1 2 2 3 3 4 4 5 5 1
样例输出
1 2 3 4 5
中文题目,就不多赘述了。
大致思路如下:
因为每个点都只能经过一次,所以解决方案是对于每个点先进行拆点,然后以mid为起点,a,b为终点建图跑网络流,然后找路径
具体思路如下:
1.拆点:
对于一个点A都可以把和它连着的路理解为有通向它的路和它通向的路,然后把通向它的路全部连到一个节点A1,它通向的路连到一个节点A2,然后让这两个节点之间连一条流为1的单向网络流。这样对于一个需要经过A点的路就从X->A->Y变成了X->A1->(这是一条从A1到A2的流为1的流)A2->Y;于是由于A1->A2的流为1所以A1->A2就只能被用一次,就保证了A只能被用一次了。
2.建图:
因为是双向边,从起点跑到mid是等价于从mid跑到起点的所以不妨把图看作从mid跑到起点和终点各一次同时保证每个点只经过一次,每个点只经过一次可以由拆点完成,然后给mid一个流为2的流,设计一个超级汇点,让起点和终点都能够流向超级汇点1的流量。 然后就可以跑网络流了
3.寻找路径:
在跑了网络流之后若G[i][j]有值那么其实说明跑过一条从j到i的流(这里要注意,如果G[i][j]和G[j][i]都有值其实相当于没有跑过),然后按照想要的输出顺序存到vector里面再读取就好了
代码如下:
#include <bits/stdc++.h>
#define inf 1000007
using namespace std ;
int G[305][305],layer[305];
int n,m,a,b,mid,E;
vector<int>ans;
bool find_layer(){
memset(layer,-1,sizeof(layer));
queue<int>q;
q.push(0);
layer[0]=0;
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=1;i<=E;i++){
if(G[now][i] && layer[i]==-1){
layer[i]=layer[now]+1;
q.push(i);
if(i==E)return true;
}
}
}
return false;
}
void dinic(){
int vis[305];
deque<int>dq;
while(!dq.empty())dq.pop_front();
while(find_layer()){
memset(vis,0,sizeof(vis));
getchar();
vis[0]=1;
dq.push_back(0);
while(!dq.empty()){
if(dq.back()==E){
int Min=inf,Min_s;
for(int i=1;i<dq.size();i++){
int s=dq[i-1],e=dq[i];
if(Min>G[s][e]){
Min=G[s][e];
Min_s=s;
}
}
for(int i=1;i<dq.size();i++){
int s=dq[i-1],e=dq[i];
G[s][e]-=Min;
G[e][s]+=Min;
}
while(!dq.empty() && dq.back()!=Min_s){
vis[dq.back()]=0;
dq.pop_back();
}
}
else{
int flag=1;
for(int i=1;i<=E;i++){
if(G[dq.back()][i] && !vis[i] && layer[i]==layer[dq.back()]+1){
flag=0;
vis[i]=1;
dq.push_back(i);
break;
}
}
if(flag)dq.pop_back();
}
}
}
}
void solve(){
int s=a;
queue<int >q;
while(!q.empty())q.pop();
q.push(s);
while(!q.empty()){//保存从a到mid的路径
int now=q.front();
q.pop();
if(now!=mid)//没有这个特判的话mid会被放两次
ans.push_back(now);
for(int i=1;i<=n;i++){
if(G[now+n][i] && !G[i+n][now]){
G[now+n][i]=0;
q.push(i);
}
}
}
while(!q.empty())q.pop();
q.push(mid);
while(!q.empty()){//保存mid到b的路径
int now=q.front();
q.pop();
ans.push_back(now);
for(int i=1;i<=n;i++){
if(G[i+n][now] && !G[now+n][i]){
G[i+n][now]=0;
q.push(i);
}
}
}
printf("%d",*ans.begin());
for(vector<int>::iterator it=ans.begin()+1;it!=ans.end();it++){
printf(" %d",*it);
}
printf("\n");
}
int main(){
int test;
scanf("%d",&test);
while(test--){
memset(G,0,sizeof(G));
scanf("%d%d%d%d%d",&n,&m,&a,&b,&mid);
E=n*2+1;//设置超级汇点
for(int i=1;i<=m;i++){
int s,e;
scanf("%d%d",&s,&e);
G[s+n][e]=G[e+n][s]=1;//对于每个点都拆成i和i+n
}
for(int i=1;i<=n;i++)G[i][i+n]=1;
G[0][mid]=2;G[mid][mid+n]=2;
G[a+n][E]=1;G[b+n][E]=1;
dinic();
ans.clear();
solve();
}
return 0;
}