1386:打击犯罪(并查集)

【题目描述】

    某个地区有n(n≤1000)个犯罪团伙,当地警方按照他们的危险程度由高到低给他们编号为1-n,他们有些团伙之间有直接联系,但是任意两个团伙都可以通过直接或间接的方式联系,这样这里就形成了一个庞大的犯罪集团,犯罪集团的危险程度由集团内的犯罪团伙数量唯一确定,而与单个犯罪团伙的危险程度无关(该犯罪集团的危险程度为n)。现在当地警方希望花尽量少的时间(即打击掉尽量少的团伙),使得庞大的犯罪集团分离成若干个较小的集团,并且他们中最大的一个的危险程度不超过n/2。为达到最好的效果,他们将按顺序打击掉编号1到k的犯罪团伙,请编程求出k的最小值。

【题目链接】

    http://ybt.ssoier.cn:8088/problem_show.php?pid=1386

【算法】

    1.二分/倍增+并查集(每次尝试都要重新构造并查集)

    2.逆序加点(维护一个并查集即可,不知道为啥,这个耗时更长。。。估计是稀疏图用邻接矩阵就很尴尬了)

【代码1】

 

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int n;
 4 int G[1100][1100],fa[1100];
 5 int Get(int x)
 6 {
 7     if(fa[x]==x) return x;
 8     return fa[x]=Get(fa[x]);
 9 }
10 void Merge(int x,int y)
11 {
12     fa[Get(x)]=Get(y);
13 }
14 bool valid(int k)
15 {
16     multiset<int> s;
17     for(int i=k+1;i<=n;i++) fa[i]=i;
18     for(int i=k+1;i<=n;i++)
19         for(int j=k+1;j<=n;j++)
20             if(G[i][j]) Merge(i,j);
21     for(int i=k+1;i<=n;i++) {
22         s.insert(Get(i));
23         if(s.count(Get(i))>n/2) return false;
24     }
25     return true;
26 }
27 int main()
28 {
29     scanf("%d",&n);
30     for(int i=1;i<=n;i++) {
31         int a,num; scanf("%d",&num);
32         while(num--) scanf("%d",&a),G[i][a]=1;
33     }
34     int l=1,r=n/2+1;
35     while(l<r) {
36         int mid=l+r>>1;
37         if(valid(mid)) r=mid;
38         else l=mid+1;
39     }
40     printf("%d\n",l);
41     return 0;
42 }

【代码2】

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int n;
 4 int fa[1100],G[1100][1100];
 5 int Get(int x)
 6 {
 7     if(x==fa[x]) return x;
 8     return fa[x]=Get(fa[x]);
 9 }
10 void Merge(int x,int y)
11 {
12     fa[Get(x)]=Get(y);
13 }
14 int main()
15 {
16     scanf("%d",&n);
17     for(int i=1;i<=n;i++) {
18         int num,a; scanf("%d",&num);
19         while(num--) scanf("%d",&a),G[i][a]=1;
20     }
21     for(int i=1;i<=n;i++) fa[i]=i;
22     for(int i=n;i>=1;i--) {
23         multiset<int> s; bool flag=1;
24         for(int j=i+1;j<=n;j++) {
25             if(G[i][j]) Merge(i,j);
26             if(G[j][i]) Merge(i,j);
27         }
28         for(int j=i;j<=n;j++) {
29             s.insert(Get(j));
30             if(s.count(Get(j))>n/2) flag=0;
31         }
32         if(!flag) { printf("%d\n",i); return 0; }
33     }
34 }

 

posted @ 2018-08-10 19:29  飞飞翔滴少年  阅读(862)  评论(0编辑  收藏  举报