[SCOI2010]连续攻击游戏 二分图匹配

题目链接

分析

桶排序

不要二分图,不要并查集
这个题最开始感觉读起来不是很难,起码一眼能看懂题意,就是给定(n)个数对,每个数对里边只能取1个数,构成数列{1……i},问(i)的最大值是多少,最开始想的是把数对排个序,然后从1开始取,看看能取到几,但发现(sort)的时间复杂度对这道题来说不可,但我就想排序,怎么办呢?于是这个时候就用到了一个时间复杂度为(O(n))的排序算法,桶排序。
然后我们只需要遍历一遍每个桶就好,但是有两个数,放哪个数还是要考虑考虑的,如果两个数都没有放过,肯定是放小的那个,因为放了大的也没用,答案更新不到它那边,如果有一个放了,肯定就放另一个,不放白不放啊。
于是,就有了下边的AC代码。。。。
但其实这个类似贪心的算法还是有问题的,问题解决可能还需要(sort),然后时间复杂度又回去了。

#include<cstdio>
#include<algorithm>
const int N=1e6+10;
int p[N];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        if(!p[a]&&!p[b])p[std::min(a,b)]++;
        else if(p[a])p[b]++;
        else p[a]++;
    }
    for(int i=1;i<=n+1;i++)
        if(!p[i]){
            printf("%d
",i-1);
            return 0;
        }
}

并查集

我们可以把每组输入转化成边,然后用并查集进行维护,最后判断一下输出就行。

#include<cstdio>
#include<cstring>
const int N=1e6+10;
int f[N],w[N],isc[N];
int find(int x){
    return f[x]==x?x:(f[x]=find(f[x]));
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n+1;i++)f[i]=i;
    for(int i=1;i<=n+1;i++)w[i]=1;
    for(int i=1;i<=n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        int fa=find(a),fb=find(b);
        if(fa==fb)isc[fa]=1;
        else {
            isc[fa]|=isc[fb];
            isc[fb]=0;
            w[fa]+=w[fb];w[fb]=0;
            f[fb]=fa;
        }
    }
    int ans;
    for(int i=1;i<=n+1;i++)
        if(!isc[find(i)]){
            if(w[find(i)]==1){
                ans=i-1;break;
            }else w[find(i)]--;
        }
    printf("%d
",ans);
}

二分图匹配

首先来说什么叫二分图,二分图简单来说就是不含奇数环的点,判断一张图是不是二分图的话,常用的是染色法,这道题就用到了
再说什么叫二分图匹配,二分图匹配说白了就是给一个男孩子集合,给定几组喜欢关系,在另外一个女孩子集合找一个点进行配对,跟函数里边的对应关系差不多。
什么叫二分图的最大匹配呢,就是让能找到女孩子的男孩子尽量都找到。
二分图的完美匹配呢?(虽然和这题没什么关系吧)就是让所有的男孩子和女孩子都成对。
这道题如果闭着眼跑一个二分图的最大匹配,你会发现WA了。
于是我们需要睁开眼跑二分图的最大匹配,但仍然会T掉,为什么呢,因为匹配了很多没有意义的匹配。
其实这个题用匈牙利算法直接匹配就行了,但是怎么建图呢?

我们看这个图,匹配的时候从(u)中取出一个值去匹配(v)中的值,因为你不能把两个男孩子跟一个女孩子配对(这不乱了吗),所以就是一对一匹配,因为每个数对只能取一个,所以可以把这两个数都加到(u)中,(v)中放上从(1-n),然后从小到大匹配就行,如果遇到不能匹配的点,输出就行。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
struct Edge{
    int to,nxt;
}e[N<<1];
int h[N],idx;
void Ins(int a,int b){
    e[idx].to=b;e[idx].nxt=h[a];h[a]=idx++;
}
int vis[N],match[N],rt;
bool dfs(int x){
    for(int i=h[x];~i;i=e[i].nxt){
        int v=e[i].to;
        if(vis[v]!=rt){
            vis[v]=rt;
            if(match[v]==-1||dfs(match[v])){
                match[v]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main(){
    memset(h,-1,sizeof(h));
    memset(match,-1,sizeof(match));
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        Ins(a,i);Ins(b,i);
    }
    for(int i=1;i<=10001;i++){
        rt=i;
        if(!dfs(i)){
            printf("%d
",i-1);
            return 0;
        }
    }
}

注意vis数组不能每次都memset,不然肯定会T掉。

原文地址:https://www.cnblogs.com/anyixing-fly/p/12822396.html