BZOJ 1854 游戏

看了以后第一反应是图论。

实在不好意思瞟了一眼题解:二分图匹配。然后我就想啦,怎么匹配呢?

显然,对应的属性要选择不同的装备,所以只要在相应的装备和属性之间连边,然后做一次二分图匹配就好了。注意,由于数字是连续的,所以一旦匹配不成功就要退出二分图匹配并输出此时的数字。

于是乎,像往常一样,写了一个朴素的匈牙利算法。TLE。试着用邻接表,邻接矩阵,各种存图的方法都T。敲打

扫了一遍,感觉不知道怎么优化啊,于是看了一眼题解,看到了神犇写法:bitset,即用bitset代替bool数组,时间竟快了一倍。

#include<vector>
#include<bitset>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
bitset<1000002>vis;
int Link[1000002];
vector<vector<int> >w(10002);
bool find(int x){
    for(int i=0;i<w[x].size();i++){
        if(!vis[w[x][i]]){
            vis[w[x][i]]=1;
            if(!Link[w[x][i]]||find(Link[w[x][i]])){
                Link[w[x][i]]=x;
                return true;
            }
        }
    }
    return false;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        w[a].push_back(i);
        w[b].push_back(i);
    }
    for(int i=1;i<=10001;i++){
        //memset(vis,0,sizeof(vis));
        vis=0;
        if(!find(i)){
            cout<<i-1<<endl;
            return 0;
        }
    }
    return 0;
}

题解中还有另一种方法:并查集。我觉得这个思维含量很高,看了很久的分析,现在让我独立的把它再分析一遍。

并查集的要求是保证祖先是这个连通分量中最大的点,并用flag[i]记录是否可以选择属性i。

1.每次读入a和b,fa和fb分别代表他的祖先。

(1).fa==fb,则无论原来的连通分量是树还是存在环,加入这条边以后一定是一个环,则每个点必定可以对应一条边,即每个属性必定和一对应一个装备。由于之前的点的flag已经更新过,所以只需flag[fa]=true即可;

(2).fa!=fb,则加入这条边以后是连接两个连通分量,且需要将祖先节点更新成max(fa,fb)。于是为了保证属性尽可能连续,所以应首先保证flag[min(fa,fb)]=1,且无论连成的连通分量是环还是树,都可以保证flag[min(fa,fb)]=1。如果之前flag[min(fa,fb)]已经为真,则说明连成的新的连通分量中肯定存在环,则可更新flag[max(fa,fb)]=1。

2.从1到10000遍历flag数组当flag[i]=0时,答案为i-1。

说的很复杂,代码很简单,如下:

#include<stdio.h>
#include<iostream>
#include<string>
#include<algorithm>
#define MAXN 1000002
using namespace std;
int f[MAXN];
int getf(int x){
    return x==f[x] ? x : f[x]=getf(f[x]);
}
bool flag[10010];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=10010;i++) f[i]=i;
    for(int i=1;i<=n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        int fa=getf(a);
        int fb=getf(b);
        if(fa==fb) flag[fa]=1;
        else {
            if(fa>fb) swap(fa,fb);
            if(!flag[fa]) flag[fa]=1;
            else flag[fb]=1;
            f[fa]=fb; 
        }
    }
    int i;
    for(i=1;i<=10010;i++) if(!flag[i]) break;
    printf("%d",i-1);
    return 0;
}






猜你喜欢

转载自blog.csdn.net/u011431896/article/details/34857335