• 关押罪犯


    原题链接:https://www.luogu.org/problem/show?pid=1525#sub

    我没记错的话之前夏令营题解我写过这个的吧?

    再写一遍好了。。还是用正常向的补集法。

    既然要让最大的冲突最小,那么我们应该优先拆冲突最高的两个人,把这两个人放到不同的监狱里才可以满足。

    那么我们应该先把所有冲突程度从大到小排序,然后依次从大到小取,如果当前取到的两个人没法安排到不同的监狱,那么他俩的冲突程度就是最后的答案了,而且因为之前排序过,所以解显然是最优的。

    补集法实际上是记录i的反面,要并查集的数组开二倍大。

    如果用i表示i号罪犯,那么我们用i+n表示不能和i在同一个监狱的罪犯。如果两个罪犯位于同一并查集,那么这两个罪犯就一定要位于同一个监狱。

    在查找冲突时发现冲突直接输出答案,如果不冲突,则把j与k+n合并,k与j+n合并。

    最后别忘了无冲突的情况,那样直接输出0就好。

    参考代码:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define maxn 200005
     6 #define maxm 1000005
     7 using namespace std;
     8 struct Edge{
     9     int from,to,dis;
    10     
    11     bool operator<(const Edge &r)const{
    12         return dis > r.dis;
    13     }
    14 };
    15 Edge edge[maxm];
    16 int father[maxn];
    17 int n,m;
    18 inline int read(){
    19     int num = 0;
    20     char c;
    21     bool flag = false;
    22     while ((c = getchar())==' ' || c=='
    ' || c== '
    ');
    23     if (c=='-')
    24         flag = true;
    25     else num = c-'0';
    26     while (isdigit(c = getchar()))
    27         num = num *10 +c-'0';
    28     return (flag?-1:1)*num;
    29     
    30 }
    31 
    32 int find(int x){
    33     if (father[x] == x)
    34         return father[x];
    35     father[x] = find(father[x]);
    36     return father[x];
    37 }
    38 
    39 void merge(int x,int y){
    40     x = find(x);
    41     y = find(y);
    42     father[y] = x;
    43 }
    44 
    45 int main(){
    46     n = read();
    47     m = read();
    48     for (int i=1; i<=n*2 ;i++)
    49         father[i] = i;
    50 
    51     for (int i=1;i<=m;i++){
    52         edge[i].from = read();
    53         edge[i].to = read();
    54         edge[i].dis = read();
    55     }
    56 
    57     sort(edge+1,edge+m+1);
    58 
    59     for (int i=1;i<=m;i++){
    60         int j = find(edge[i].from);
    61         int k = find(edge[i].to);
    62         if (j == k){
    63             cout << edge[i].dis << endl;
    64             return 0;
    65         }
    66         if (j == find(edge[i].to + n) || k == find(edge[i].from + n))
    67             continue;
    68 
    69         merge(j,k+n);
    70         merge(k,j+n);
    71     }
    72     cout << "0" << endl;
    73     return 0;
    74 }
    这题要求最大的早恋事件影响力最小,那么很显然我们要考虑尽量把搞事情影响力比较大的俩人拆到两个宿舍里。我们首先对这些搞事情的度排序,然后去模拟着把搞事情度最高的两个人分到不同的宿舍里,只要在拆开两个人的时候发现和已有的拆分相矛盾,那么这肯定就是最小的影响力最大事件了。
    这要怎么实现呢?并查集!
    这里并查集的应用比较巧妙。如果设有n个人,那么我们的并查集应该开到2n大小。就是网上题解所谓的“补集法”。i表示第i个学生,它的补集i+n表示不能和i在同一个宿舍的人。然后我们每次读入a和b的搞事情程度为c,就把a和b+n所在集合合并,a+n与b所在集合合并。
    要注意,每次合并之前都要判断ab是不是在同一集合,如果是,那么这就是最小的最大冲突。
  • 相关阅读:
    20191024-1 每周例行报告
    20191017-1 每周例行报告
    20191010-2 每周例行报告
    梁梦瑶 20190919-4 单元测试
    交换机配置VLAN Cisco packet
    20191128-1 总结
    20191121-1 每周例行报告
    20191114-1 每周例行报告
    对组内成员的感谢博客
    每周例行报告
  • 原文地址:https://www.cnblogs.com/OIerShawnZhou/p/7545992.html
Copyright © 2020-2023  润新知