计蒜客 菜鸟物流的运输网络 解题报告

来源:互联网 发布:weui商城模板源码 编辑:程序博客网 时间:2024/05/01 00:59

菜鸟物流有自己的运输网络,网络中包含 nn 个城市物流集散中心,和m 对城市之间的运输线路(线路是双向的)。菜鸟物流允许淘宝卖家自行确定包裹的运输路径,但只有一条限制规则:不允许经过重复的城市。淘宝卖家小明从a 城市寄出快递后,希望包裹在 mid 城市进行包装加工以后再寄往 b 城市。

现在小明希望算出一个满足他需求的合法运输路径,你可以帮他算出来么?

已知这样的方案一定存在。请为小明输出任意一个可行方案。

输入格式

第一行一个正整数 T(1≤T≤10)T(1 \leq T \leq 10)T(1T10) 表示数据的组数。

每组数据第一行 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(3n100,m2n(n1)),表示城市个数和运输线路数目。

第二行 3 个互不相同正整数a,b,mid(1≤a,b,mid≤n)a,b,mid(1 \leq a,b,mid \leq n)a,b,mid(1a,b,midn),表示起点、终点和途径城市。

接下来 m 行,每行2 个正整数x,y(1≤x,y≤n)x,y(1\leq x,y \leq n)x,y(1x,yn),表示每条线路连接的2 个城市。

每组数据一定存在至少一组合法方案。如果有多种满足小明需求的合法运输路径,输出任意一个即可。

输出格式

每组数据输出 L 个正整数,表示顺次经过的城市的编号,包括起点和终点。每两个整数之间一个空格,最后一个整数后面没有空格。

样例输入

15 51 5 31 22 33 44 55 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 1000007using 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;}



原创粉丝点击