【Codeforces】 Round #145 E. Road Repairs(最小树形图模板+路径输出)

题目链接

题目大意:现在有N个城市,标号为1的为我们的首都,我们现在想着从首都能够到达任意一个城市,现在给出你M个边,也就是城市之间的道路,其中这些道路有好的不需要修理,有坏了的需要修理,问你最少需要修几条路才能使我们能够从首都到达任意一个城市,注意给出的边均为单向边

思路:这里是最小树型图的思想,在网上看了代码还有的是直接的搜索,好像不大好,

更多的是朱刘算法的最小树型图吧,但是路径的输出比较难,这个地方搞了很久,个人也是比较菜

我们标记一个可能会删掉的边,根据那个图中的我们找出来最小的边集合后可能会出现环,环中的某些边不是需要的,

参考

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=2001005;
const int inf=1000000005;
struct Edge
{
    int u,v,cost,id;
    int w;
} e[maxn];
struct Cancel
{
    int pre,id;
} cancel[maxn];
int vis[maxn],pre[maxn],id[maxn],in[maxn];
int useE[maxn],preid[maxn];
void addedge(int u,int v,int w,int id)
{
    e[id].u=u;
    e[id].v=v;
    e[id].id=id;
    e[id].cost=e[id].w=w;
}
int Directed_MST(int root,int Nv,int Ne)
{
    int ret=0,total=Ne;
    while(true)
    {
        for(int i=0; i<Nv; i++)
            in[i]=inf;
        memset(pre,-1,sizeof(pre));
        for(int i=0; i<Ne; i++)
        {
            int u=e[i].u,v=e[i].v;
            if(e[i].cost<in[v]&&u!=v)
            {
                pre[v] = u;
                in[v] = e[i].cost;
                preid[v]=e[i].id;
            }
        }
        for(int i=0; i<Nv; i++)
            if(i!=root&&in[i]==inf)
                return -1;
        int cntnode=0;
        memset(id,-1,sizeof(id));
        memset(vis,-1,sizeof(vis));
        in[root]=0;
        for(int i=0; i<Nv; i++)
        {
            ret+=in[i];
            int v=i;
            if(i!=root) useE[preid[i]]++;
            while(vis[v] != i && id[v] == -1 && v != root)
                vis[v] = i,v = pre[v];
            if(v!=root&&id[v]==-1)
            {
                for(int u = pre[v]; u != v; u = pre[u])
                    id[u] = cntnode;
                id[v]=cntnode++;
            }
        }
        if(cntnode==0)
            break;
        for(int i=0; i<Nv; i++)
            if(id[i]==-1)
                id[i]=cntnode++;
        for(int i=0; i<Ne; i++)
        {
            int u=e[i].u;
            int v = e[i].v;
            e[i].u = id[u];
            e[i].v = id[v];
            if(e[i].u != e[i].v)
            {
                cancel[total].id=e[i].id;
                cancel[total].pre=preid[v];
                e[i].cost -= in[v];
                e[i].id=total++;
            }
        }
        Nv=cntnode;
        root=id[root];
    }
    for(int i=total-1; i>=Ne; i--)
        if(useE[i])
        {
            useE[cancel[i].pre]--;
            useE[cancel[i].id]++;
        }
    return ret;
}
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0; i<m; i++)
    {
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        x--,y--;
        addedge(x,y,c,i);
    }
    int Min=Directed_MST(0,n,m);
    printf("%d\n",Min);

    if(Min!=-1)
    {
        for(int i=0;i<m;i++)
            if(useE[i]&&e[i].w)
                printf("%d ",i+1);
    }

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值