zoukankan      html  css  js  c++  java
  • [BZOJ 1854] [Scoi2010] 游戏

    题目链接:BZOJ - 1854

    题目分析

    方法一:

    这道题目有一种巧妙的使用并查集的做法 : 我们把每个数看作一个点,那么开始时每个点单独作为一个集合。对于每个卡片 (a,b) ,就是 a 与 b 之间连了一条边。(这里不是卡片是武器...不过都一样)

    那么在一个联通分量中,如果这个联通分量点数为 n ,边不存在环,那么可以满足任选 n-1 个点(显然选最小的 n-1 个最优,即不选最大的那个)。如果存在环,那么所有 n 个点都可满足。

    对于每个 (a,b) ,我们先判断 a 与 b 是否已经联通,如果是,那么这就出现了环,a与b所在的集合的最大元素就也可以取了。

    如果不联通,就把较小的一个集合的最大元素设为可以取,再将两个集合联通。这里一定要注意的是,如果较小的集合的最大元素已经可以取了,那么说明小集合含有环,那么联通后的整个集合也存在环,所以就把较大集合的最大元素设为可以取,这个十分易忽视,但是数据弱所以也不会WA... 注意:我们要求每个集合的代表元素为最大的元素,所以合并的时候注意顺序,不能用什么按秩合并之类的优化。

    最后从 1 开始一直向后一个个看能否取,如果不能取了就停止,输出答案。

    方法二:

    二分图匹配,将武器看作一个点集,属性值看作另一个点集,将每个武器与两个属性值连边,这样就可以匹配了,因为每个武器只能使用一次。

    我们从1开始,试图为 i 找到匹配,如果找不到就停止,输出答案。

    匈牙利算法每次寻找匹配之前要清空Used数组,直接清显然TLE,所以用时间戳。

    代码

    并查集:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    const int MaxN = 1000000 + 5;
    
    int n, a, b;
    int f[MaxN];
    
    bool Can[MaxN];
    
    int Find(int x) {
        int i, j, k;
        j = x; while (j != f[j]) j = f[j];
        i = x;
        while (i != j) {
            k = i;
            i = f[i];
            f[k] = j;
        }
        return j;
    }
    
    void Esun(int x, int y) {
        if (Can[x]) Can[y] = true;
        else Can[x] = true;
        f[x] = y;
    }
    
    int main()
    {
        scanf("%d", &n);
        for (int i = 1; i <= 10005; ++i) f[i] = i;
        memset(Can, 0, sizeof(Can));
        for (int i = 1; i <= n; ++i) {
            scanf("%d%d", &a, &b);
            int Fa = Find(a), Fb = Find(b);
            if (Fa > Fb) swap(Fa, Fb);
            if (Fa == Fb) Can[Fa] = true;
            else Esun(Fa, Fb);
        }
        int Ans = 0;
        while (Can[Ans + 1]) ++Ans;
        printf("%d
    ", Ans);
        return 0;
    }
    

    二分图匹配:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    const int MaxN = 1000000 + 5, MaxNum = 10000 + 5;
    
    int n, a, b, Used_Index;
    int f[MaxN], Used[MaxNum];
    
    struct Edge 
    {
    	int v;
    	Edge *Next;
    } E[MaxN * 2], *P = E, *Point[MaxN];
    
    inline void AddEdge(int x, int y) {
    	++P; P -> v = y;
    	P -> Next = Point[x]; Point[x] = P;
    }
    
    bool Find(int x) {
    	for (Edge *j = Point[x]; j; j = j -> Next) {
    		if (Used[j -> v] != Used_Index) {
    			Used[j -> v] = Used_Index;
    			if (f[j -> v] == 0 || Find(f[j -> v])) {
    				f[j -> v] = x;
    				return true;
    			}
    		}
    	}
    	return false;
    }
    
    int main() 
    {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; ++i) {
    		scanf("%d%d", &a, &b);
    		AddEdge(a, i);
    		AddEdge(b, i);
    	}
    	Used_Index = 0;
    	for (int i = 1; i <= 10001; ++i) {
    		++Used_Index;
    		if (!Find(i)) {
    			printf("%d
    ", i - 1);
    			break;
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    ios 分组列表头部空白处理
    滑动cell 显示的按钮 类型分别是 删除 置顶 其他
    获取View所在的控制器
    MySQL联表查询
    Yii的常用URL和渲染方法
    Ubuntu下为Firefox安装Adobe Flash Player
    ubuntu下的nginx+php+mysql配置
    ubuntu下配置nginx+php
    如何使用PHP操作cookie
    ubuntu broadcom无线驱动安装
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4194731.html
Copyright © 2011-2022 走看看