Codeforces 240E Road Repairs(最小树形图路径)

题目链接:
Codeforces 240E Road Repairs
题意:
有n个城市,编号1–n,首都编号为1,有m条有向边u[i],v[i],w[i],w[i]=0表示这条边是完好的,w[i]=1表示这条边需要修理,问从首都出发能到达任意城市最少需要修多少条边?如果不能到达任意城市输出-1,否则输出需要修复的最少边数和边的编号。如果有多组答案输出任意一组。
分析:
输出最小树形图的路径。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const int MAX_N = 100010;
const int MAX_M = 2000010;
const int INF = 0x7fffffff;

int n, m, NV, NE;
int pre[MAX_N], vis[MAX_N], In[MAX_N], ID[MAX_N];
int usedEdge[MAX_M], preEdge[MAX_N];

struct Edge{
    int u, v, w, ww, id;

    Edge() {}
    Edge(int _u, int _v, int _w, int _ww, int _id) : u(_u), v(_v), w(_w), ww(_ww), id(_id) {}
}edge[MAX_M];

struct Used{
    int pre, id;
}cancle[MAX_M];


int ZLEdmonds(int root)
{
    memset(usedEdge, 0, sizeof(usedEdge));
    int res = 0, u, v, w;
    int total = NE;
    while(1){
        for(int i = 0; i < NV; i++) { In[i] = INF; }
        for(int i = 0; i < NE; i++){
            u = edge[i].u, v = edge[i].v, w = edge[i].w;
            if(u != v && w < In[v]){
                In[v] = w;
                pre[v] = u;
                //记录这个顶点所在的边编号
                preEdge[v] = edge[i].id;
            }
        }
        for(int i = 0; i < NV; i++){
            if(i != root && In[i] == INF) return -1;
        } 
        int cnt = 0;
        memset(vis, -1, sizeof(vis));
        memset(ID, -1, sizeof(ID));
        In[root] = 0;
        for(int i = 0; i < NV; i++){
            res += In[i];
            v = i;
            if(i != root){ //非根节点
                usedEdge[preEdge[i]]++; //这个顶点所在的边被使用
                int t = preEdge[i];
            }
            while(v != root && vis[v] != i && ID[v] == -1){
                vis[v] = i;
                v = pre[v];
            }
            if(v != root && ID[v] == -1){
                for(u = pre[v]; u != v; u = pre[u]){
                    ID[u] = cnt;
                }
                ID[v] = cnt++;
            }
        }
        if(cnt == 0) break;
        for(int i = 0; i < NV; i++){
            if(ID[i] == -1) ID[i] = cnt++;
        }
        for(int i = 0; i < NE;i ++){
            u = edge[i].u, v = edge[i].v;
            edge[i].u = ID[u], edge[i].v = ID[v];
            //将原先的边的id重新编号
            if(edge[i].u != edge[i].v){ 
            //edge[i].u 和edge[i].v 是新图的点编号,两者不相等说明两者在原图中不属于同一有向环
                edge[i].w -= In[v];
                //如果在新图中用到该边那么上一次图中用到这条边就要被取消
                cancle[total].id = edge[i].id;
                cancle[total].pre = preEdge[v];
                //重新编排新图的边的编号
                edge[i].id = total++;
            }
        }
        NV = cnt;
        root = ID[root];
    }
    for(int i = total - 1; i >= NE; i--){ //从后往前扫新建边编号
        if(usedEdge[i]){ //如果这条边被使用
            usedEdge[cancle[i].id]++; //这条边的使用情况+1
            usedEdge[cancle[i].pre]--; //上一次用到这条边被取消
            //相当于在上一次图中的有向环中的这个指向v顶点的有向边被取消
        }
    }
    return res;
}


int main()
{
    while(~scanf("%d%d", &n, &m)){
        for(int i = 0; i < m; i++){
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            u--, v--;
            edge[i] = Edge(u, v, w, w, i);
        }
        NV = n, NE = m;
        int ans = ZLEdmonds(0);
        if(ans == -1 || ans == 0) printf("%d\n", ans);
        else {
            printf("%d\n", ans);
            for(int i = 0; i < m; i++){
                if(edge[i].ww == 1 && usedEdge[i]) printf("%d ", i + 1);
            }
            printf("\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值