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

这篇博客介绍了一种使用网络流算法解决菜鸟物流运输路径问题的方法。通过拆点、建图和寻找路径,确保每个城市仅经过一次,并找到从起点到途径城市再到终点的合法运输路径。样例输入和输出展示了问题的具体实例和解决方案的思路。
摘要由CSDN通过智能技术生成

菜鸟物流有自己的运输网络,网络中包含 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 个正整数,表示顺次经过的城市的编号,包括起点和终点。每两个整数之间一个空格,最后一个整数后面没有空格。

样例输入
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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值