Road Repairs(CF-240E)

Problem Description

A country named Berland has n cities. They are numbered with integers from 1 to n. City with index 1 is the capital of the country. Some pairs of cities have monodirectional roads built between them. However, not all of them are in good condition. For each road we know whether it needs repairing or not. If a road needs repairing, then it is forbidden to use it. However, the Berland government can repair the road so that it can be used.

Right now Berland is being threatened by the war with the neighbouring state. So the capital officials decided to send a military squad to each city. The squads can move only along the existing roads, as there's no time or money to build new roads. However, some roads will probably have to be repaired in order to get to some cities.

Of course the country needs much resources to defeat the enemy, so you want to be careful with what you're going to throw the forces on. That's why the Berland government wants to repair the minimum number of roads that is enough for the military troops to get to any city from the capital, driving along good or repaired roads. Your task is to help the Berland government and to find out, which roads need to be repaired.

Input

The first line contains two space-separated integers n and m (1 ≤ n, m ≤ 105) — the number of cities and the number of roads in Berland.

Next m lines contain three space-separated integers ai, bi, ci (1 ≤ ai, bi ≤ n, ai ≠ bi, 0 ≤ ci ≤ 1), describing the road from city ai to city bi. If ci equals 0, than the given road is in a good condition. If ci equals 1, then it needs to be repaired.

It is guaranteed that there is not more than one road between the cities in each direction.

Output

If even after all roads are repaired, it is still impossible to get to some city from the capital, print  - 1. Otherwise, on the first line print the minimum number of roads that need to be repaired, and on the second line print the numbers of these roads, separated by single spaces.

The roads are numbered starting from 1 in the order, in which they are given in the input.

If there are multiple sets, consisting of the minimum number of roads to repair to make travelling to any city from the capital possible, print any of them.

If it is possible to reach any city, driving along the roads that already are in a good condition, print 0 in the only output line.

Examples

Input

3 2
1 3 0
3 2 1

Output

1
2

Input

4 4
2 3 0
3 4 0
4 1 0
4 2 1

Output
-1

Input

4 4
4 3
1 2 0
1 3 0
1 4 0

Output
0

题意:n 个点 m 条边的有向图,每条边权值为 0、1,0 代表能走,1 代表不能走需要修复,现在要求从 1 号点能到达所有点,问要修复的路的最小代价,如果不能到达输出 -1

思路:

题目实质上是一个最小树形图,要求输出选取了要修的路径

在使用朱刘算法求解最小树形图的时候,一开始选择的必然是最短的边,但这样一来,无可避免会选取到环,那就需要在朱刘算法的过程中删除环

在朱刘算法过程中,大体分为三个步骤:求最短弧、找环、缩环三个步骤,在收缩环的过程中,选取弧的过程的时候就是减去环中对应点的最小入边,因此,直接加进去这条边即可,当这条边可能不是最优的边,之后可能会遇到更优解,那么就要删除这条边,并加入新边

那么就需要做一个判断,记录上一个改点的最小入边,并将他删除,从而达到加入新边,此时可以利用两个数组分别记录,再图逐步缩环不断衍生推到最小树形图后,对删除边和加入边两个数组进行标记,最后根据标记输出即可

Source Program

#include<bits/stdc++.h>
#define EPS 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
const int MOD = 1E9+7;
const int N = 1000000+5;
const int dx[] = {-1,1,0,0,-1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;
struct Edge{
    int x,y;
    int w;
    int id;//边编号
    int real;//
}edge[N];
int vis[N];
int id[N];
int in[N],pre[N];
int lastEdge[N];//每个点的最小入边编号
int addEdge[N],deleteEdge[N];//添边与删边
int used[N];//标记选择的边
int zhuLiu(int root,int n,int m){
    int res=0;
    int edgeNum=m;
    while(true){
        for(int i=1;i<=n;i++)
            in[i]=INF;

        //寻找每个点的最小入边
        for(int i=1;i<=m;i++){
            int x=edge[i].x;
            int y=edge[i].y;
            if(edge[i].w<in[y] && x!=y){
                pre[y]=x;
                in[y]=edge[i].w;

                lastEdge[y]=edge[i].id;//记录边编号
            }
        }

        //判断是否存在最小树形图
        for(int i=1;i<=n;i++){
            if(i==root)
                continue;
            if(in[i]==INF)
                return -1;
        }


        //寻找所有的环
        int cnt=0;
        in[root]=0;
        memset(id,-1,sizeof(id));
        memset(vis,-1,sizeof(vis));
        for(int i=1;i<=n;i++){
            res+=in[i];

            //收缩环过程中找到边,加边
            if(i!=root)
                used[lastEdge[i]]++;

            int y=i;
            while(vis[y]!=i&&id[y]==-1&&y!=root){
                vis[y]=i;
                y=pre[y];
            }

            if(y!=root&&id[y]==-1){
                cnt++;
                for(int x=pre[y];x!=y;x=pre[x])
                    id[x]=cnt;
                id[y]=cnt;
            }
        }
        //无环
        if(cnt==0)
            break;
        //可能存在独立点
        for(int i=1;i<=n;i++)
            if(id[i]==-1)
                id[i]=++cnt;

        //建立新图,缩点重新标记
        for(int i=1;i<=m;i++){
            int x=edge[i].x;
            int y=edge[i].y;
            edge[i].x=id[x];
            edge[i].y=id[y];

            if(id[x]!=id[y]){
                edge[i].w-=in[y];

                //缩点过程中,删除上一个改点的最小入边,以达到加入新边的作用
                deleteEdge[++edgeNum]=lastEdge[y];
                addEdge[edgeNum]=edge[i].id;
                edge[i].id=edgeNum;
            }
        }

        n=cnt;//以环数为下次操作的点数,继续上述操作,直到无环
        root=id[root];
    }
    for(int i=edgeNum;i>m;i--){
        if(used[i]){
            used[deleteEdge[i]]--;
            used[addEdge[i]]++;
        }
    }
    return res;
}
int main(){
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].w);
        edge[i].id=i;
        edge[i].real=edge[i].w;
    }
    int res=zhuLiu(1,n,m);
    if(res==-1||res==0)
        printf("%d\n",res);
    else{
        printf("%d\n",res);
        for(int i=1;i<=m;i++){
            if(used[i]&&edge[i].real){
                printf("%d ",i);
            }
        }
        printf("\n");
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值