tarjin求割点

题目: hdu3671

http://acm.hdu.edu.cn/showproblem.php?pid=3671

题意:给一个无向图,要求毁掉两个点,使图变得不连通,图一开始是连通的

因为要毁掉两个点,就不是简单的求割点,再看看数据范围,点数为1000,边数为10000,Tarjan的时间复杂度为O(E),如果用枚举法,先枚举要毁掉的第一个点,再用Tarjan进行处理来找割点会不会超时呢?答案是不会,时间为O(v*E),刚好是千万级别,不超

做法:先枚举要删除的第1个点,在原图中删除它,看看删除它后整个图的变化

        1.整个图变得不连通了(即这个点本身是割点),但是还没完要分类讨论一下

        (1).整个图变为两部分,但是两部分刚好都是一个点,那么这两个点再毁掉哪个点都好,图的连通分支数都不

会增加,这是一个特殊情况

      例如,(1,2)(2,3)这种图,是无解的,任意毁掉两个点都无法增加图的连通分支,所以方案数为0

        (2).整个图分为两部分,但是有一部分的点数为1,另一部分大于1,那么这时候只要在较大的那部分,任意毁掉一个点(无论是不是割点都行),最后整个图都会至少被分为了两个部分

                (如果毁掉的是割点,将分成更多份),所以这样产生的方案数是V-2

        (3).整个图分为了两个部分,两个部分的点数都大于1,那么任意在哪个部分毁掉那个点都可以(无论是不是割点都行),最后整个图都会至少分为两个部分,所以方案数为V-1

        (4).整个图被分为了三个或更多的部分,那么也是在剩下的点中任意毁掉一个点都可以(无论那个点是不是割点),方案数为V-1

              (如果这个点刚好处于一个部分且这个部分只有它自己一个点,那么    毁掉后整个图的分支数减1;如果这个点在一个部分且这个部分不止它一个点且这个点不是割点,那么分支数  不会增加,如果是割点分支数为增加)

     2.删除第一个点后,整个图还是连通的(是连通,不是双连通)

        那么就在剩下的图中找割点,找到几个,方案数就是多少

最后注意一点,这样计算的结果,很容易想到是有重复的,但是不难想到,其实刚好重复了一次,因为对于一个图,方案是固定的,枚举了所有点,找出了所有已方案,相当于每个方案算了两次,最后答案除2即可

 1 #include<iostream>
 2 #include<cstring>
 3 //#include<bits/stdc++.h>
 4 #include<math.h>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<stack>
 8 #include<cstdio>
 9 #include<map>
10 #include<set>
11 #define  si(a)       scanf("%d",&a)
12 #define  sl(a)       scanf("%lld",&a)
13 #define  sii(a,b)    scanf("%d%d",&a,&b)
14 #define  sll(a,b)    scanf("%lld%lld",&a,&b)
15 #define  queues      priority_queue
16 #define mod 1000000007
17 #define mem(a)  memset(a,0,sizeof(a));
18 #define def(a) ((a)&(-a))
19 #define mp make_pair
20 #define pb push_back
21 #define  fi first
22 #define se second
23 typedef long long ll;
24 const int INF=0x3f3f3f3f;
25 using namespace std;
26 const int MAX=1003;
27 vector<int>q[MAX];
28 int DFN[MAX];
29 int VIM[MAX];
30 int top;
31 int clore;
32 int num;
33 set<int>s;
34 void tarjan(int a,int fa,int d)
35 {
36     int child=0;
37     DFN[a]=VIM[a]=++top;
38     num++;
39     for(int i:q[a])
40     {
41         if(i==fa||i==d)continue;
42         if(!DFN[i])
43         {
44             child++;
45             tarjan(i,a,d);
46             VIM[a]=min(VIM[a],VIM[i]);
47             if(VIM[i]>=DFN[a]&&a!=fa)
48                 s.insert(a);
49         }
50         else VIM[a]=min(VIM[a],DFN[i]);
51     }
52     if(a==fa&&child>=2)s.insert(a);
53 }
54 int main()
55 {
56     int n,m;
57     int z=0;
58     while(cin>>n>>m&&n+m)
59     {
60         int sum=0;
61         top=0;
62         for(int i=1; i<=n; i++)
63             q[i].clear();
64         for(int i=1; i<=m; i++)
65         {
66             int a,b;
67             cin>>a>>b;
68             q[a].push_back(b);
69             q[b].push_back(a);
70         }
71         for(int i=1; i<=n; i++)
72         {
73             int count1=0;
74             int ok=0;
75             clore=0;
76             s.clear();
77             mem(VIM);
78             mem(DFN);
79             for(int j=1; j<=n; j++)
80                 if(j!=i&&!DFN[j])
81                 {
82                     count1++;
83                     num=0;
84                     tarjan(j,j,i);
85                     if(num==1)ok++;
86                 }
87             if(count1>=3)sum+=n-1;
88             else if(count1==2&&ok==1)sum+=n-2;
89             else if(count1==2&&ok==0)sum+=n-1;
90             else if(count1==1)sum+=s.size();
91         }
92         printf("Case %d: %d
",++z,sum/2);
93     }
94 
95 }
View Code
原文地址:https://www.cnblogs.com/zxz666/p/10722663.html