bzoj 1854: [Scoi2010]游戏 (并查集||二分图最大匹配)

链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1854

写法1: 二分图最大匹配

思路:  将武器的属性对武器编号建边,因为只有10000种属性,我们直接对1-10000跑二分图匹配,同时用时间戳优化匹配。

实现代码:

#include<bits/stdc++.h>
using namespace std;
const int M = 1e6+10;
vector<int>g[M];
int vis[M],pre[M],t,n;
bool dfs(int u){
    for(int i = 0;i < g[u].size();i ++){
        int v = g[u][i];
        if(vis[v] ^ t){
            vis[v] = t;
            if(!pre[v]||dfs(pre[v])){
                pre[v] = u;
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
    int u,v;
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++){
        scanf("%d%d",&u,&v);
        g[u].push_back(i);
        g[v].push_back(i);
    }
    for(int i = 1;i <= 10001;i ++){
        ++t;
        if(!dfs(i)){
            printf("%d\n",i-1);
            break;
        }
    }
    return 0;
}

思路2: 并查集

把每个武器的属性a,b连成一条边,这样就会形成一幅图,对于图中的每个联通块来说,如果当前联通块没有环,那么一定会有任意p-1个点满足条件(p为联通块大小),如果联通块中存在环的话,那么这p个点都满足条件,我们将p个点都标记下,如果没有环的话,我们不选择最大的那个点,因为属性是从小到大选的,优先选择小的,维护的话,并查集合并的时候将顶点小的并查集并到顶点大的并查集,并且标记掉小的并查集的顶点。

实现代码;

#include<bits/stdc++.h>
using namespace std;
const int M = 1e6+10;
int f[M],vis[M],n;
int Find(int x){
    if(x == f[x]) return x;
    return f[x] = Find(f[x]);
}

void mix(int x,int y){
     int fx = Find(x),fy = Find(y);
     if(fx == fy){
        vis[fx] = 1;
     }
     else {
        if(fx < fy) swap(fx,fy);
        vis[fy] = 1;
        f[fy] = fx;
     }
}

int main()
{
    int u,v;
    scanf("%d",&n);
    for(int i = 0;i <= 10001;i ++) f[i] = i;
    for(int i = 1;i <= n;i ++){
        scanf("%d%d",&u,&v);
        mix(u,v);
    }
    for(int i = 1;i <= 10001;i ++){
        if(!vis[i]){
            printf("%d\n",i-1);
            break;
        }
    }
    return 0;
}

 

posted @ 2019-03-22 18:07  冥想选手  阅读(174)  评论(0编辑  收藏  举报