无向图边连通性 ZOJ2588 Burning Bridges

 Burning Bridges
Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu

Description

Ferry Kingdom is a nice little country located on N islands that are connected by M bridges. All bridges are very beautiful and are loved by everyone in the kingdom. Of course, the system of bridges is designed in such a way that one can get from any island to any other one.

But recently the great sorrow has come to the kingdom. Ferry Kingdom was conquered by the armies of the great warrior Jordan and he has decided to burn all the bridges that connected the islands. This was a very cruel decision, but the wizards of Jordan have advised him no to do so, because after that his own armies would not be able to get from one island to another. So Jordan decided to burn as many bridges as possible so that is was still possible for his armies to get from any island to any other one.

Now the poor people of Ferry Kingdom wonder what bridges will be burned. Of course, they cannot learn that, because the list of bridges to be burned is kept in great secret. However, one old man said that you can help them to find the set of bridges that certainly will not be burned.

So they came to you and asked for help. Can you do that?


Input

The input contains multiple test cases. The first line of the input is a single integer T (1 <= T <= 20) which is the number of test cases. T test cases follow, each preceded by a single blank line.

The first line of each case contains N and M - the number of islands and bridges in Ferry Kingdom respectively (2 <= N <= 10 000, 1 <= M <= 100 000). Next M lines contain two different integer numbers each and describe bridges. Note that there can be several bridges between a pair of islands.


Output

On the first line of each case print K - the number of bridges that will certainly not be burned. On the second line print K integers - the numbers of these bridges. Bridges are numbered starting from one, as they are given in the input.

Two consecutive cases should be separated by a single blank line. No blank line should be produced after the last test case.


Sample Input

2

6 7
1 2
2 3
2 4
5 4
1 3
4 5
3 6

10 16
2 6
3 7
6 5
5 9
5 4
1 2
9 8
6 4
2 10
3 8
7 9
1 4
2 4
10 5
1 6
6 10

Sample Output

2
3 7

1
4 

题目描述:
Ferry王国是一个漂亮的岛国,一共有N个岛国、M座桥,通过这些桥可以从每个小岛都能
到达任何一个小岛。很不幸的是,最近Ferry王国被Jordan征服了。Jordan决定烧毁所有的桥。
这是个残酷的决定,但是Jordan的谋士建议他不要这样做,因为如果烧毁所有的桥梁,他自己的
军队也不能从一个岛到达另一个岛。因此Jordan决定烧尽可能多的桥,只要能保证他的军队能从
任何一个小岛都能到达每个小岛就可以了。
现在Ferry王国的人民很想知道哪些桥梁将被烧毁。当然,他们无法得知这些信息,因为哪
些桥将被烧毁是Jordan的军事机密。然而,你可以告知Ferry王国的人民哪些桥肯定不会被烧毁。
输入描述:
输入文件中包含多个测试数据。输入文件中第1行为一个整数T,1≤T≤20,表示测试数据
的数目。接下来有T个测试数据,测试数据之间用空行隔开。
每个测试数据的第1行为两个整数N和M,分别表示岛的数目和桥的数目,2≤N≤10000,
1≤M≤100000;接下来有M行,每行为两个不同的整数,为一座桥所连接的小岛的编号。注意,
两个岛之间可能有多座桥。 
图论算法理论、实现及应用
- 402 - 
输出描述:
对每个测试数据,首先在第1行输出一个整数K,表示K座桥不会被烧毁;第2行输出K个
整数,为这些桥的序号。桥的序号从1开始计起,按输入的顺序进行编号。
两个测试数据的输出之间有一个空行。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define min(a,b) ((a) > (b) ? (b) : (a))
using namespace std;

struct node //边表的形式记录边
{
    int v,num;
    int next;
};
struct node edge[200005];
int head[10005],ind;//ind代表边的数目
int deep[10005];//存储深度优先数
int ans[10005];//ans[i]为顶点i可达祖先的最小编号
int mark[10005];//标记是否被遍历过
int c[200005],tn;//记录割边以及割边的数目

void dfs(int u,int fnum,int d)//u代表当前搜索的顶点,fnum代表当前顶点的父亲顶点,d代表搜索深度
{
    mark[u] = 1;
    deep[u] = d;
    //ans[u]取的是三项的最小值,第一项是其本身的深度优先数,
    //第二项是它直接通过回边可以到达的最低优先数
    //第三项是它的子女顶点w的ans[w]的最小值,因为他的子女可以到达的最低深度优先数,它也可以通过子女到达
    //这三项分别对应下面对于ans的三次赋值
    ans[u] = d;
    int i,j,k;
    for(i = head[u]; i != -1; i = edge[i].next)//一次遍历和u相连的边
    {
        if(edge[i].num == fnum)//该边所在的序号和其父顶点所在的序号相等,说明之间有连边,存在环的情况
            continue;
        if(mark[edge[i].v] == 1)//该顶点已经被遍历过了,说明之前存在过最小编号,重新计算可达祖先的最小编号
        {
            ans[u] = min(ans[u],deep[edge[i].v]);
        }
        else//对于没有被遍历过的点,深入下去,继续DFS
        {
            dfs(edge[i].v,edge[i].num,d+1);
            ans[u] = min(ans[u],ans[edge[i].v]);
            //顶点u是关节点的充要条件:u或者是具有两个以上子女的深度优先生成树的根,或者虽然不是一个根
            //但它有一个子女w,使得ans[w] >= deep[u];
            //ans[w] >= deep[u]含义:顶点u的子女顶点w,能够通过如前所述的路径到达顶点的最低深度优先数大于等于顶点的
            //深度优先数(注意在深度优先生成树中,顶点m是顶点n的祖先,则必定有deep[m] < deep[n]),即w及其子孙
            //不存在指向顶点u的祖先的回边。这时,删除顶点u及其所关联的边,则以顶点w为根的子树就从搜索树中脱离了
            if(ans[edge[i].v] > deep[u])
            {
                c[tn++] = edge[i].num+1;
            }
        }
    }
}

int main()
{
    int T,n,m,a,b;
    int i,j,k;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        ind = 0;
        for(i = 0; i < m; i++)
        {
            scanf("%d%d",&a,&b);
            a--;
            b--;
            edge[ind].v = b;
            edge[ind].num = i;
            edge[ind].next = head[a];
            head[a] = ind++;

            edge[ind].v = a;
            edge[ind].num = i;
            edge[ind].next = head[b];
            head[b] = ind++;
        }
        tn = 0;
        memset(mark,0,sizeof(mark));
        dfs(0,-1,1);//从顶点0出发进行搜索,顶点0是根节点,所以代表其父亲节点的第二个参数为-1
        printf("%d\n",tn);
        sort(c,c+tn);
        for(i = 0; i < tn; i++)
        {
           if(i)
           printf(" ");
           printf("%d",c[i]);
        }
        if(tn)
            printf("\n");
        if(T)
            printf("\n");
    }

    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值