SGU252 Railway Communication(二分图最大权匹配)

本文介绍了一种针对铁路网络中的列车运行线优化算法。通过构建二分图并使用Kuhn-Munkres算法(KM算法)求解最大权匹配问题,以达到在满足特定约束条件的情况下,使列车运行线数量最少及总长度最短的目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题意:
             某国有 n n 个城镇,m条单向铁路。每条铁路都连接着两个不同的城镇,且该铁路系统中不存在环。现需要确定一些列车运行线,使其满足:
             1   1     每条铁路最多属于一条列车运行线;
             2   2     每个城镇最多被一条列车运行线通过(通过包括作为起点或终点);
             3   3     每个城镇至少被一条列车运行线通过;
             4   4     列车运行线的数量应尽量小。
             5   5     在满足以上条件下列车运行线的长度和应该尽量小。


思路:
             05 05 年的论文题, 将每个点 u u 拆成入点u和出点 u u ,对于(u,v)这条有向边, u u v连接一条有向边, 权值为该路径长度的负值,然后就是求一个二分图最大权匹配, 求得的最大值的负数值即为原路径长度最小值。然后找一下求解出来的路径, 一条路径如果是起点,那么它只有出点, 也就是说 u u 是起点的话, 只有u有匹配边而 u u ′ 没有匹配边,同样 u u 如果是终点的话只有u有匹配边而 u u <script type="math/tex" id="MathJax-Element-3938">u</script>没有,寻找一下就好了, 注意有可能只有一个点的情况。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<iostream>
typedef long long ll;
const int maxn = 1e2 + 10;
const int INF = 1e9;
using namespace std;

typedef pair<int, int> pa;
vector<pa> G[maxn];

int w[maxn][maxn], n;
int lx[maxn], ly[maxn];
int from[maxn], m;
bool S[maxn], T[maxn];

bool match(int i) {
    S[i] = true;
    for(int j = 1; j <= n; j++) {
        if(lx[i] + ly[j] != w[i][j] || T[j]) continue;
        T[j] = true;
        if(!from[j] || match(from[j])) {
            from[j] = i;
            return true;
        }
    }
    return false;
}

void update() {
    int a = 1 << 30;
    for(int i = 1; i <= n; i++) {
        if(!S[i]) continue;
        for(int j = 1; j <= n; j++) {
            if(T[j]) continue;
            a = min(a, lx[i] + ly[j] - w[i][j]);
        }
    }
    for(int i = 1; i <= n; i++) {
        if(S[i]) lx[i] -= a;
        if(T[i]) ly[i] += a;
    }
}

void KM() {
    for(int i = 1; i <= n; i++) {
        from[i] = lx[i] = ly[i] = 0;
        for(int j = 1; j <= n; j++) {
            lx[i] = max(lx[i], w[i][j]);
        }
    }
    for(int i = 1; i <= n; i++) {
        while(1) {
            for(int j = 1; j <= n; j++) S[j] = T[j] = 0;
            if(match(i)) break;
            else update();
        }
    }
}

int vis[maxn], to[maxn];
vector<int> vec[maxn];

int main() {
    while(scanf("%d %d", &n, &m) != EOF) {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++) w[i][j] = -INF;
        for(int i = 1; i <= m; i++) {
            int u, v, c;
            scanf("%d %d %d", &u, &v, &c);
            w[u][v] = -c;
        }
        KM();
        int ans = 0, num = 0;
        for(int i = 1; i <= n; i++) if(w[from[i]][i] != -INF) ans += w[from[i]][i];
        memset(vis, 0, sizeof vis);
        memset(to, -1, sizeof to);
        for(int i = 0; i < maxn; i++) vec[i].clear();
        for(int i = 1; i <= n; i++) {
            if(w[from[i]][i] != -INF) {
                to[from[i]] = i;
                vis[i] = 1;
            }
        }
        for(int i = 1; i <= n; i++) {
            if(to[i] == -1 && !vis[i]) { vec[num].push_back(i); num++; continue; }
            if(~to[i] && !vis[i]) { ///起始点
                int x = i;
                while(~x) {
                    vec[num].push_back(x);
                    x = to[x];
                }
                num++;
            }
        }
        printf("%d %d\n", num, -ans);
        for(int i = 0; i < num; i++) {
            printf("%d", vec[i].size());
            for(int j = 0; j < vec[i].size(); j++) printf(" %d", vec[i][j]);
            printf("\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值