【并查集--补集法】分宿舍+食物链

UPC 2020年春混合个人训练 5.14日场

时间限制: 1 Sec 内存限制: 128 MB
题目描述
A校有着神奇的住宿制度,不分男女宿舍,所有n个学生被统一分到两栋宿舍楼中。作为年轻人,学生之间心生爱慕之情是很正常。我们用爱慕值来表示两名学生之间的爱慕程度,如果两名爱慕值为c的学生被安排在同一宿舍楼,他们或她们便会在一起,并造成影响力为c的早恋事件。
每年年末,身为政教处主任的你会将所有早恋事件按照影响力从大到小排成一个列表,然后上报给校长。公务繁忙的校长只会去看列表中第一个事件的影响力,如果影响很大,他会考虑撤换政教处主任。
在详细考察了n个学生之间的爱慕关系后,你觉得压力很大。你要合理的将学生们分到两栋宿舍,以求产生的早恋事件影响力都比较小,以保住自己的官职。假设只要处于同一栋宿舍楼的两个人之间有爱慕关系,他们就一定会在这年的某个时候在一起。
那么,要怎么分配,才能让校长看到的那个早恋事件的影响力最小呢?这个最小值是多少?
输入
第一行两个整数n和m,分别表示学生的数目和爱慕关系的对数。
接下来m行,每行为3个正整数ai,bi,ci,表示学生ai和bi之间有爱慕关系,爱慕值为ci。
数据保证1≤ai≤bi≤n,0<ci≤109,且每对爱慕关系只出现一次。
输出
输出一个数,为通过合理安排,校长看到的那个早恋事件的最小影响力。如果没有发生早恋事件,输出0。
样例输入
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
样例输出
3512
提示
对于100%的数据,n≤20000,m≤100000。
解题思路:这里可以非常巧妙的用并查集中补集法来做,拿样例来说吧,首先肯定是对爱慕值降序排列,从大往小遍历遇见第一个与前面的关系矛盾的,直接输出,否则输出0就好了,在代码中用样例再具体解释一下吧
AC源:

/**
	用样例来说,排完序后是:
	1 2 28351
	3 4 12884
	1 3 6618
	2 3 3512
	1 4 2534
	2 4 1805
	1和2不在一块,3和4不在一块,1和3不在一块;2和3不在一块,这句话显然与前面的矛盾了,
	怎样把它筛出来呢,用普通的并查集恐怕是办不到,
	我们可以将并查集的范围扩大一倍,假设i和i+n一定是不在一块的,扩大的那一倍称为虚拟父节点,用样例来说,1和2不在一块,那么1和6在一块,2和5在一块;
	3和4不在一块,那么3和8在一块,4和7在一块;1和3不在一块,那么1和7在一块,3和5在一块;
	这样就可以利用补集巧妙地将1 4 6 7,2 3 5 8规划到一个连通块中,当遇到1 4时,发现他俩在一个并查集中,
	自然与前面的话矛盾,输出就好啦
**/
#include<bits/stdc++.h>
#include<iostream>
#include<stack>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 1e6 + 10;
typedef long long ll;
typedef pair<int,int> PII;
priority_queue<int,vector<int>,greater<int> >q;
int n,m;
int pre[maxn];
struct node{
    int l,r,w;
}a[maxn];
bool cmp(node x,node y){
    return x.w>y.w;
}
int Find(int x){
    if(x==pre[x]) return x;
    else return pre[x]=Find(pre[x]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=2*n;i++) pre[i]=i;
    for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].w);
    sort(a+1,a+1+m,cmp);
    for(int i=1;i<=m;i++)
    {
        if(Find(a[i].l)==Find(a[i].r))   ///每次先判断一下是不是在一个并查集中
        {
            printf("%d",a[i].w);
            return 0;
        }
        else{
            pre[Find(a[i].l+n)]=Find(a[i].r);
            pre[Find(a[i].r+n)]=Find(a[i].l);
        }
    }
    puts("0");   ///都满足的话输出0
    return 0;
}

这里再附赠例题一道
传送门
思路一:并查集补集法
AC源:

#include<iostream>
using namespace std;
const int maxn = 1e6+ 7;
int n,k,ans;
int pre[maxn];   ///范围1:自己的同类  范围2:食物   范围3:天敌
int Find(int x){
    if(pre[x]==x) return x;
    else return pre[x]=Find(pre[x]);
}
int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=3*n;i++) pre[i]=i;
    while(k--){
        int d,x,y;
        scanf("%d%d%d",&d,&x,&y);
        if(x>n||y>n)
        {
            ans++;
            continue;
        }
        if(d==1){
            if(Find(x+n)==Find(y)||Find(x)==Find(y+n)) ans++;
            else pre[Find(x)]=Find(y),pre[Find(x+n)]=Find(y+n),pre[Find(x+2*n)]=Find(y+2*n);
        }
        else{
            if(Find(x)==Find(y)||Find(x)==Find(y+n)) ans++;
            else pre[Find(x+n)]=Find(y),pre[Find(x+2*n)]=Find(y+n),pre[Find(x)]=Find(y+2*n);
        }
    }
    cout << ans;
    return 0;
}

思路二:普通并查集+思维
AC源:

#include <iostream>
using namespace std;
const int maxn = 1e6 +7;
int n,m;
int pre[maxn],d[maxn];
int ans=0;
int c,x,y;
int Find(int x){
    if(pre[x]!=x)
    {
        int t=Find(pre[x]);
        d[x]+=d[pre[x]];
        pre[x]=t;
    }
    return pre[x];
}
int main(){
    cin >> n >> m;
    for(int i=1;i<=n;i++) pre[i]=i;
    while(m--){
        cin >> c >> x >> y;
        if(x>n||y>n) ans++;
        else{
            int fx=Find(x),fy=Find(y);
            if(c==1)
            {
                if(fx == fy && (d[x]-d[y])%3) ans++;
                else if(fx != fy)
                {
                    pre[fx]=fy;
                    d[fx]=(d[y]-d[x])%3;
                }
            }
            else{
                if(fx == fy && (d[x]-d[y]-1)%3) ans++;
                else if(fx != fy)
                {
                    pre[fx]=fy;
                    d[fx]=(d[y]-d[x]+1)%3;
                }
            }
        }
    }
    cout << ans;
    return 0;
}

愿所有汗水都有收获,所有努力都不被辜负~~加油吧少年

猜你喜欢

转载自blog.csdn.net/zhazhaxiaosong/article/details/106168093