Day6 && Day7图论

 并查集

 

 

A - How Many Answers Are Wrong

题意:
已知区间[1,n],给出m组数据,即[l,r]区间内数据之和为s,求错误数据的数量。

拿到这道题,真的没思路,知道用并查集,但是关于如何定义s迟迟没有思路,就开始了搜文章学习(划水)的历程。

下面就记录一些自己的体会:

1、并查集中路径压缩的过程中如何更新关系域是关键;

2、根据数据定义一些合理的关系(如:集合中的根与集合中的元素的关系),才能对集合进行合并。这里可能会分一些情况讨论,最终应该是可以化简的。

然后放出学习的文章(感谢作者):

1、hdu 3038(扩展并查集)

 

2、POJ-1182 食物链

里面有关向量的思考和设计很巧妙

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 const int maxn = 200020;
 6 int f[maxn];
 7 int sum[maxn];   //记录当前结点到根的距离
 8 int find(int x){
 9 //return x==f[x]?x:find(f[x]);    
10     if(x != f[x]){
11         int roota = f[x];
12         f[x] = find(f[x]);
13         sum[x] += sum[roota];
14         //x->rootb = x->roota + roota->rootb 
15     }
16     return f[x];
17 }
18 int main(){
19     //freopen("in.txt","r",stdin); 
20     int n,m;
21     while(scanf("%d%d", &n, &m) != EOF){
22         for(int i=0; i<=n; i++){
23             f[i] = i;
24             sum[i] = 0;
25         }
26         int ans = 0;
27         while(m--){
28             int a, b, v;
29             scanf("%d%d%d", &a, &b, &v);
30             a--;
31             //join 
32             int roota = find(a);
33             int rootb = find(b);
34             if(roota == rootb){
35                 if(sum[a]-sum[b] != v)    ans++;
36             }else{
37                 f[roota] = rootb;
38                 //定义大的数字是小的数字的根 
39                 sum[roota] = -sum[a] + sum[b] + v;
40                 //变换成等式 sum[roota] + sum[a] = sum[b] + v;
41                 //a->roota + roota->rootb == b->rootb + a->b 
42             }
43             //join
44         }
45         printf("%d\n",ans);
46     }
47     return 0;
48 }
View Code

 

 

B - 食物链

摘出上博客中的一句话:

对于集合里的任意两个元素x,y而言,它们之间必定存在着某种联系,因为并查集中的元素均是有联系的(这点是并查集的实质),否则也不会被合并到当前集合中。 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int maxn = 50010;
 6 int f[maxn], relation[maxn];
 7 //relation[i]:i到根的偏移量 
 8 int sum;
 9 void init(int n){
10     for(int i=0; i<n; i++){
11         f[i] = i;
12         relation[i] = 0;
13     }
14     sum = 0;
15 }
16 int find(int x){
17     if(x != f[x]){
18         int rootx = f[x];
19         f[x] = find(rootx);
20         //路径压缩更新关系域//这是关键1 
21         relation[x] = (relation[x] + relation[rootx]) % 3;
22     }
23     return f[x];
24 }
25 void join(int x, int y, int d){
26     int rootx = find(x);
27     int rooty = find(y);
28     if(rootx != rooty){
29         f[rooty] = rootx; 
30         relation[rooty] = (3 + d-1 + relation[x] - relation[y]) % 3;
31         //此处d-1题中给出的询问已知条件即0同类,1吃,2被吃
32         /*先用等式表达:
33         这里并非简单的加减,实质应该是关系的转移,可以用向量理解 
34              r->rx + x->y = y->ry + ry->rx
35         ====>r[x] + k = r[y] + ry->rx
36         ====>ry->rx = r[x] + k - r[y]
37            k = d - 1 
38            同理下面两式也得 
39         */ 
40     }else{
41         if((d==1) && (relation[x] != relation[y])){
42             sum++;
43         }else if((d==2) && ((d-1) != (relation[y] - relation[x] + 3) % 3)){
44             sum++;
45         }
46     }
47 }
48 int main(){
49     //freopen("in.txt","r",stdin); 
50     int n, k;
51     scanf("%d%d", &n, &k);
52     init(n);
53     for(int i=0; i<k; i++){
54         int d, x, y;
55         scanf("%d%d%d",&d, &x, &y);
56         if(x > n || y > n){
57             sum++;
58         }else if((d==2) && (x == y)){
59             sum++;
60         }else{
61             join(x, y, d);
62         }
63     }
64     printf("%d\n",sum);
65     return 0;
66 }
View Code

 

 

C - A Bug's Life

题意:给定n个bugs,编号依次1、2、……n。它们之间只有雄性和雌性之分,并给定m对交配的情况,根据这m对交配的情况,判断是否有同性恋出现的情况。

Thinking:

定义relation[i]:结点i到根的距离:即相对与根节点的性别。

x,y在同一集合,他们相对根的距离即相对根的性别,而他们又同根,易得x,y的相对性别

x,y不再同一集合,思考向量得关系: y->x + x->rootx = y->rooty + rooty->rootx 其中y->x应为1。 

 1 #include <cstdio>
 2 const int maxn = 2010;
 3 int f[maxn];
 4 int relation[maxn];
 5 bool flag;
 6 void init(int n){
 7     //这里n没有初始完( i < n )导致WA了半天 
 8     for(int i=0; i<=n; i++){
 9         f[i] = i;
10         relation[i] = 0;
11     }
12 }
13 int find(int x){
14     if(x != f[x]){
15         int rootx = f[x];
16         f[x] = find(rootx);
17         relation[x] = (relation[rootx] + relation[x]) % 2;
18     }
19     return f[x];
20 }
21 void join(int x, int y){
22     int rootx = find(x);
23     int rooty = find(y);
24     if(rootx == rooty){
25         if(relation[x] == relation[y]){
26             flag = true;
27         }
28     }else{
29         /*A了这题后在网上看到很多解法中这里对x,y进行分类, 
30         即 x<y  : 
31                 f[rooty] = rootx;
32                 relation[rooty] = ((relation[x]+1) + relation[y]) % 2;
33           x>y    :
34                    f[rootx] = rooty;
35                 relation[rootx] = ((relation[y]+1)%2 + relation[x]) % 2;
36             这里我认为应该可以不需要,因为查找过程中进行了路径压缩 
37         */ 
38         f[rooty] = rootx;
39         relation[rooty] = ((relation[x]+1) - relation[y]) % 2;
40             //这里+由向量y->x + x->rootx = y->rooty + rooty->rootx得应该是-,但%2好像算术上是等价的
41             //这里我个人认为是减,当然%2时加和减结果一样, 这里留待思考????? 
42     }
43 }
44 int main(){
45     //freopen("in.txt","r",stdin); 
46     int T;
47     scanf("%d", &T);
48     for(int t=1; t<=T; t++){
49         int b, inter;
50         scanf("%d%d",&b, &inter);
51         init(b);
52         flag = false;
53         for(int i=0; i<inter; i++){
54             int x, y;
55             scanf("%d%d", &x, &y);
56             if(flag){
57                 continue;
58             }else{
59                 join(x, y);
60             }
61         }
62         printf("Scenario #%d:\n", t);
63         if(flag){
64             printf("Suspicious bugs found!\n\n");
65         }else{
66             printf("No suspicious bugs found!\n\n");
67         }
68     }
69     return 0;
70 }
View Code

 

 

D - Supermarket 

并查集,不太想做了,以后补

 


无向图的割点

 

 

E - SPF

题意:一个网络中割点的个数,和割点可以将网络分成几部分。

Thinking:

不用belong记录强连通分量集合的编号,需要加入parent[]记录父节点,child判断子树数量。这是为了分开计算割点是根 和非根的情况。

割点的判断:number[u]<=low[v]即v必须通过u才能访问u的祖先,这里u就是割点,可以参考上面列的第二篇文章。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int maxn = 1005;
 6 struct Node{
 7     int to;
 8     int next;
 9 }edge[maxn*maxn];
10 int head[maxn], E, V;
11 void add_edge(int u, int v){
12     edge[E].to = v;    edge[E].next = head[u];    head[u] = E++;
13     edge[E].to = u;    edge[E].next = head[v];    head[v] = E++;
14     V = max(max(u,v), V);
15 }
16 int size_[maxn];
17 // 记录删除i结点后的连通分量个数 
18 int low[maxn], number[maxn], parent[maxn];
19 ////Let LOWPT(v) be the smallest vertex in the set ${v}\bigcap { w| v \xrightarrow{*} -\rightarrow w}$ 
20 ///number[u]为结点u搜索的次序编号
21 ///parent[i]:记录i的父亲结点 
22 bool flag; ///是否有割点存在 
23 int cnt;
24 
25 void dfs(int u){
26     int child = 0;
27     //记录子树的数量 
28     low[u] = number[u] = ++cnt;
29     for(int i=head[u]; i!=-1; i=edge[i].next){
30         int v = edge[i].to;
31         if(!number[v]){
32             child++;
33             parent[v] = u;
34             dfs(v);
35             ///dfs(v)更新low[v] 
36             low[u] = min(low[u], low[v]);
37             //必须通过u,v才能访问u的祖先number[u]<=low[v]
38             if((parent[u]==-1 && child>=2) || (parent[u]!=-1 && number[u]<=low[v])){
39                 /*
40                 child>2保证如果是根节点,将比非根节点少加1 
41                 因为如果割点不是根节点,其连通分量要加上其父辈 
42                 */ 
43                 flag = true;
44                 size_[u]++;
45             }
46         }else{ 
47             low[u] = min(low[u], number[v]);
48         }
49     }
50 }
51 void init(){
52     memset(size_, 0, sizeof(size_));
53     memset(number, 0, sizeof(number));
54     memset(low, 0, sizeof(low));
55     memset(head, -1, sizeof(head));
56     memset(parent, -1, sizeof(parent));
57     E = V = 0;
58     flag = false;
59     cnt = 0;
60 } 
61 int main(){
62 //    freopen("in.txt","r", stdin); 
63     int cas;
64     for(int tt=1; ;tt++){
65         init();
66         //input
67         int u, v=-1;
68         while(scanf("%d", &u) && u){
69             scanf("%d", &v);
70             add_edge(u, v);
71         }
72         if(v == -1)    break;
73         //input
74         dfs(V);
75         printf("Network #%d\n",tt);
76         if(flag){
77             for(int i=1; i <= V; i++){
78                 if(size_[i] > 0){
79                     printf("  SPF node %d leaves %d subnets\n",i,size_[i]+1);
80                 }
81             }
82         }else{
83             printf("  No SPF nodes\n");
84         }
85         printf("\n");
86     }
87     return 0;
88 }
View Code

 

update(2018-08-30):

1、 pre[v]<pre[u]&&v!=f 理解:在无向图中edge(u, f)不是反向边(第一次处理时从后代指向祖先的边),只是树边edge(f, u)的第二次访问。

2、  if(lowv > pre[u]) { u->v is bridge} 这是对桥的判断。

3、在无向连通图G的dfs树中,非根结点u是G的割点当且仅当u存在一个子节点v,使得v及其所有后代都没有反向边连回u的祖先(连回u不算)。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <stack>
 7 using namespace std;
 8 
 9 const int maxn = 1010;
10 vector<int> G[maxn];
11 
12 int iscut[maxn];
13 
14 int low[maxn], pre[maxn];
15 //pre:时间戳
16 //low[u]:u及其后代所能连回的最早祖先的pre值 
17 int cnt, V;
18 
19 int dfs(int u, int f){
20     int lowu = pre[u] = ++cnt;
21     int child = 0;   //子节点数目 
22     for(int i=0; i<G[u].size(); i++){
23         int v = G[u][i];
24         if(!pre[v]){   //未访问v 
25             child++;
26             int lowv = dfs(v, u);
27             lowu = min(lowv, lowu);   //用后代low更新low u 
28             if(lowv >= pre[u]){
29                 iscut[u]++;
30             }
31         }else if(pre[v]<pre[u] && v!=f){
32         //v==f无向图,此边不属于反向边,属于树边的二次访问 
33             lowu = min(lowu, pre[v]);  //反向边更新u 
34         }
35     }
36     if(f<0 && child==1){ //根节点特判
37         iscut[u] = 0;    
38     }
39     return low[u] = lowu;
40 }
41 void add_edge(int a, int b){
42     G[a].push_back(b); G[b].push_back(a);
43     V = max(max(a, b), V);
44 }
45 void init(){
46     memset(pre, 0, sizeof(pre));
47     memset(iscut, 0, sizeof(iscut));
48     for(int i=0; i<maxn; i++){  G[i].clear();  }
49     cnt = 0;  V = 0;
50 }
51 int main(){
52     //freopen("in.txt", "r", stdin);
53     for(int tt=1; ;tt++){
54         init();
55         int u, v=-1;
56         while(scanf("%d", &u) && u){
57             scanf("%d", &v);
58             add_edge(u, v);
59         }
60         if(v == -1) break;
61         dfs(V, -1);
62         printf("Network #%d\n", tt);
63         bool flag = true;
64         for(int i=0; i<=V; i++){
65             if(iscut[i]){
66                 printf("  SPF node %d leaves %d subnets\n", i, iscut[i]+1);
67                 flag = false;
68             }
69         }
70         if(flag){
71             printf("  No SPF nodes\n");
72         }puts("");
73     }
74     return 0;
75 }
76 
77  
View Code

 

 

 


 

有向图的强连通分量

 

 

完成了一个Tarjan的算法实现,第一次写,有待优化。

View Code

 

F - Proving Equivalences

题意:给一个有向图,试求添加多少条边可以使该图成为强连通图。

Thinking:

要利用DAG的性质,则需要用Tarjan算法缩点,将有向图转为DAG。

至于添加多少条边,可以这样想,怎样操作能构成最大环,即将DAG头尾相连即可。即求入度和出度的max();

需要特判强连通分量个数为一的情况如(1,2)(2,1),则不需要加边。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <stack>
 5 using namespace std;
 6 const int maxn = 20010;
 7 struct Node{
 8     int to;
 9     int next;
10 }edge[maxn * 3];
11 int head[maxn],cnt;
12 stack<int> S;
13 int low[maxn], number[maxn];
14 int belong[maxn], scc;
15 //保存每个点属于的强连通集合的编号;  强连通分量编号最大值(即个数
16 void add_edge(int u, int v){
17     edge[++cnt].to = v;
18     edge[cnt].next = head[u];
19     head[u] = cnt;
20 }
21 void dfs(int u){
22     S.push(u);
23     number[u] = low[u] = ++cnt;
24     for(int i = head[u]; i!=-1; i = edge[i].next){
25         int v = edge[i].to;
26         if(!number[v]){
27             dfs(v);
28             low[u] = min(low[u], low[v]);
29         }else if(!belong[v]){
30             low[u] = min(low[u], number[v]);
31         }
32     }
33     if(number[u] == low[u]){
34         scc++;
35         int v;
36         do{
37             v = S.top(), S.pop();
38             belong[v] = scc;
39         }while(u != v);
40     }
41 }
42 int in[maxn], out[maxn];
43 void init(){
44     cnt = scc = 0;
45     memset(head, -1, sizeof(head));
46     memset(number, 0, sizeof(number));
47     memset(low, 0, sizeof(low));
48     memset(in, 0, sizeof(in));
49     memset(out, 0, sizeof(out));
50     memset(belong, 0, sizeof(belong));
51     while(!S.empty())    S.pop();
52 }
53 int main(){
54     //freopen("in.txt", "r", stdin);
55     int T;
56     scanf("%d", &T);
57     while(T--){
58         init();
59         int n, m;
60         scanf("%d%d", &n, &m);
61         for(int i=0; i<m; i++){
62             int u, v;
63             scanf("%d%d", &u, &v);
64             add_edge(u, v);
65         }
66         cnt = 0;
67         for(int i=1; i<=n; i++){
68             if(!number[i]){
69                 dfs(i);
70             }
71         }
72         if(scc == 1){
73             printf("0\n");
74             continue;
75         }
76         for(int i=1; i<=n; i++){
77             for(int j=head[i]; j!=-1; j=edge[j].next){
78                 int v = edge[j].to;
79                 if(belong[v] != belong[i]){
80                     in[belong[v]]++;
81                     out[belong[i]]++;
82                 }
83             }
84         }
85         int ans1 = 0, ans2 = 0;
86         for(int i=1; i<=scc; i++){
87             if(!out[i])    ans1++;
88             if(!in[i])    ans2++;    
89         }
90         printf("%d\n",max(ans1, ans2));
91     }
92     return 0;
93 }
View Code

 

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int maxn = 20010;
 5 vector<int> G[maxn];
 6 stack<int> S;
 7 int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt;
 8 //sccno[i]: i所在的SCC编号
 9 void dfs(int u){
10     pre[u] = lowlink[u] = ++dfs_clock;
11     S.push(u);
12     for(int i=0; i<G[u].size(); i++){
13         int v = G[u][i];
14         if(!pre[v]){
15             dfs(v);
16             lowlink[u] = min(lowlink[u], lowlink[v]);
17         }else if(!sccno[v]){
18             lowlink[u] = min(lowlink[u], pre[v]);
19         }
20     }
21     if(lowlink[u] == pre[u]){
22         scc_cnt++;
23         while(true){
24             int x = S.top();  S.pop();
25             sccno[x] = scc_cnt;
26             if(x == u) break;
27         }
28     }
29 }
30 int find_scc(int n){
31     dfs_clock = scc_cnt = 0;
32     memset(sccno, 0, sizeof(sccno));
33     memset(pre, 0, sizeof(pre));
34     for(int i=0; i<n; i++){
35         if(!pre[i]) dfs(i);
36     }
37 }
38 int in[maxn], out[maxn];
39 
40 int main(){
41     //freopen("in.txt", "r", stdin); 
42     int t; scanf("%d", &t);
43     while(t--){
44         int n, m;  scanf("%d%d", &n, &m);
45         for(int i=0; i<n; i++) G[i].clear();
46         for(int i=0; i<m; i++){
47             int a, b; scanf("%d%d", &a, &b); a--, b--;
48             G[a].push_back(b);
49         }
50         find_scc(n);
51         // 对缩点后的DAG入度和出度计算
52         //in[i]:i的入度为0 
53         for(int i=1; i<=scc_cnt; i++) in[i] = out[i] = 1;
54         for(int u=0; u<n; u++){
55             for(int i=0; i<G[u].size(); i++){
56                 int v = G[u][i];
57                 if(sccno[u] != sccno[v]) in[sccno[v]] = out[sccno[u]] = 0;
58                 //u,v对应的出入度不为0 
59             }
60         }
61         // 
62         int a = 0, b = 0;
63         for(int i=1; i<=scc_cnt; i++){
64             if(in[i])  a++;
65             if(out[i]) b++;
66         }
67         int ans = max(a, b);
68         if(scc_cnt == 1) ans = 0;
69         printf("%d\n", ans);
70     }
71     return 0;
72 }
View Code

 

 

 

G - The Largest Clique

题意:给一个有向图G,求一个结点数最大的结点集(任意两点相互可达)。

Targan缩点 + DAG上的最长路。 给一个含圈的有向图,求最长路。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int maxn = 1010;
 5 vector<int> G[maxn], mp[maxn];
 6 stack<int> S;
 7 int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt;
 8 //sccno[i]: i所在的SCC编号
 9 void dfs(int u){
10     pre[u] = lowlink[u] = ++dfs_clock;
11     S.push(u);
12     for(int i=0; i<G[u].size(); i++){
13         int v = G[u][i];
14         if(!pre[v]){
15             dfs(v);
16             lowlink[u] = min(lowlink[u], lowlink[v]);
17         }else if(!sccno[v]){
18             lowlink[u] = min(lowlink[u], pre[v]);
19         }
20     }
21     if(lowlink[u] == pre[u]){
22         scc_cnt++;
23         int x;
24         do{
25             x = S.top();  S.pop();
26             sccno[x] = scc_cnt;
27         }while(x != u);
28     }
29 }
30 void find_scc(int n){
31     dfs_clock = scc_cnt = 0;
32     memset(sccno, 0, sizeof(sccno));
33     memset(pre, 0, sizeof(pre));
34     for(int i=0; i<n; i++){
35         if(!pre[i]) dfs(i);
36     }
37 }
38 int size_[maxn], d[maxn];
39 
40 //DAG上的最长距离 
41 int dp(int u) {
42     if(d[u] >= 0) return d[u];
43     d[u] = size_[u];
44     for(int i = 0; i < mp[u].size(); i++){
45         int v = mp[u][i];
46         d[u] = max(d[u], dp(v) + size_[u]);
47     }
48     return d[u];
49 }
50 void build(int n){
51     for(int i=0; i<=scc_cnt; i++) mp[i].clear();
52     memset(size_, 0, sizeof(size_));
53     for(int i=0; i<n; i++) size_[sccno[i]]++;
54     for(int i=0; i<n; i++){
55         for(int j=0; j<G[i].size(); j++){
56             int v = G[i][j];
57             if(sccno[i] != sccno[v]){
58                 mp[sccno[i]].push_back(sccno[v]);
59             }
60         }
61     }
62 } 
63 int main(){
64 //    freopen("in.txt", "r", stdin); 
65     int t; scanf("%d", &t);
66     while(t--){
67         int n, m;  scanf("%d%d", &n, &m);
68         for(int i=0; i<n; i++) G[i].clear();
69         for(int i=0; i<m; i++){
70             int a, b; scanf("%d%d", &a, &b); a--; b--;
71             G[a].push_back(b);
72         }
73         
74         find_scc(n);
75         
76         build(n);
77         
78         int ans = 0;
79         memset(d, -1, sizeof(d));
80         for(int i=1; i<=scc_cnt; i++){
81             ans = max(ans, dp(i));
82         }
83         printf("%d\n", ans);
84     }
85     return 0;
86 } 
View Code

 

 


 

无向图的双连通分量

K - Knights of the Round Table(uva 3523)

题意:n个骑士举行会议,每次圆桌会议至少三人,且骑士数目为奇数,相互憎恨的骑士不能再相邻位置,问多少个骑士不能参加会议。

二分图 + BCC

建图:两个骑士可以相邻,则建一条无向边。

求不在任何一个简奇圈上的结点个数。

简单圈上的所有结点必然属于同一个双连通分量;二分图没有奇圈。

 

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int maxn = 1010;
  4 
  5 struct edge{
  6     int u, v;
  7     edge(int a, int b) : u(a), v(b) {}
  8 }; 
  9 int pre[maxn], iscut[maxn], bccno[maxn], dfs_clock, bcc_cnt;
 10 //bccno[i]=x:第i个顶点属于第x个点-双连通分量 
 11 vector<int> G[maxn], bcc[maxn];
 12 //bcc[i]: 编号为i的点-双连通分量的所有结点 
 13 stack<edge> S;
 14 
 15 int dfs(int u, int f){
 16     int lowu = pre[u] = ++dfs_clock;
 17     int child = 0;
 18     for(int i=0; i<G[u].size(); i++){
 19         int v = G[u][i];
 20         edge e = (edge){u, v};
 21         if(!pre[v]){
 22             S.push(e);
 23             child++;
 24             int lowv = dfs(v, u);
 25             if(lowv >= pre[u]){
 26                 iscut[u]++;
 27                 bcc_cnt++; bcc[bcc_cnt].clear();
 28                 while(true){
 29                     edge x = S.top(); S.pop();
 30                     if(bccno[x.u] != bcc_cnt){
 31                         bcc[bcc_cnt].push_back(x.u);
 32                         bccno[x.u] = bcc_cnt;
 33                     }
 34                     if(bccno[x.v] != bcc_cnt){
 35                         bcc[bcc_cnt].push_back(x.v);
 36                         bccno[x.v] = bcc_cnt;
 37                     }
 38                     if(x.u == u && x.v == v){
 39                         break;
 40                     }
 41                 }
 42             }
 43         }else if(pre[v] < pre[u] && v != f){
 44             S.push(e);
 45             lowu = min(lowu, pre[v]);
 46         }
 47     }
 48     if(f < 0 && child==1) iscut[u]=0;
 49     return lowu;
 50 }
 51 
 52 void find_bcc(int n){
 53     memset(pre, 0, sizeof(pre));
 54     memset(iscut, 0, sizeof(iscut));
 55     memset(bccno, 0,  sizeof(bccno));
 56     dfs_clock = bcc_cnt = 0;
 57     for(int i=0; i<n; i++){
 58         if(!pre[i]) dfs(i, -1);
 59     }
 60 }
 61 int color[maxn];
 62 bool odd[maxn];
 63 bool bipartite(int u, int b){
 64     for(int i=0; i<G[u].size(); i++){
 65         int v = G[u][i];
 66         if(bccno[v] != b) continue;
 67         if(color[v] == color[u]){
 68             return false;
 69         }
 70         if(!color[v]){
 71             color[v] = 3 - color[u];
 72             if(!bipartite(v, b)) return false;
 73         }
 74     }
 75     return true;
 76 }
 77 int A[maxn][maxn];
 78 
 79 int main(){
 80     //freopen("in.txt", "r", stdin);
 81     int kase = 0, n, m;
 82     while(scanf("%d%d", &n, &m) != EOF && n){
 83         for(int i=0;i<n;i++)G[i].clear();
 84         memset(A, 0, sizeof(A));
 85         
 86         for(int i=0;i<m;i++){
 87             int a,b; scanf("%d%d",&a, &b); a--,b--;
 88             A[a][b] = A[b][a] = 1;            
 89         }
 90         for(int i=0; i<n; i++){
 91             for(int j=i+1; j<n; j++){
 92                 if(!A[i][j]) G[i].push_back(j), G[j].push_back(i);
 93             }
 94         }
 95         
 96         find_bcc(n);
 97         
 98         memset(odd, 0, sizeof(odd));
 99         for(int i=1; i<=bcc_cnt; i++){
100             for(int j=0; j<bcc[i].size(); j++){  //同一个BBC内统一编号 
101                 bccno[bcc[i][j]] = i;
102             }
103             int u = bcc[i][0];
104             
105             memset(color, 0, sizeof(color));
106             color[u] = 1;
107             if(!bipartite(u, i)){   //不是二分图,标记为奇圈 
108                 for(int j=0; j<bcc[i].size(); j++){
109                     odd[bcc[i][j]] = true;
110                 }
111             }
112         }
113         int ans = n;
114         for(int i=0; i<n; i++) if(odd[i]) ans--;
115         printf("%d\n", ans);
116     }
117     return 0;
118 }
View Code

 

 

 

 

转载于:https://www.cnblogs.com/seaupnice/p/9440137.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值