BZOJ 1854 游戏(二分图匹配或并查集)

此题的二分图匹配做法很容易想,就是把属性当做s集,武器当做t集,如果该武器拥有该武器则连一条边。

那么答案就是求该二分图的最大前i个匹配。将匈牙利算法改一改,当前找不到增广路就break。

但是过这个题需要常数优化,不能每次都fillchar一遍used数组。可以用队列将使用的used点加入,然后需要初始化的时候弹出即可。

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi 3.1415926535
# define eps 1e-9
# define MOD 1000000009
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())==‘-‘) flag=1;
    else if(ch>=‘0‘&&ch<=‘9‘) res=ch-‘0‘;
    while((ch=getchar())>=‘0‘&&ch<=‘9‘)  res=res*10+(ch-‘0‘);
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar(‘-‘); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+‘0‘);
}
const int N=1000005;
//Code begin...

struct Edge{int to, next;}edge[N<<1];
int head[N], tot, linker[N], uN;
bool used[N];
queue<int>Q;

void init(){tot=0; mem(head,-1);}
void addedge(int u, int v){
    edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++;
}
bool dfs(int u){
    for (int i=head[u]; i!=-1; i=edge[i].next) {
        int v=edge[i].to;
        if (!used[v]) {
            used[v]=true;
            Q.push(v);
            if (linker[v]==-1||dfs(linker[v])){linker[v]=u; return true;}
        }
    }
    return false;
}
int hungary(){
    int res=0;
    mem(linker,-1);
    FOR(u,1,uN) {
        while (!Q.empty()) {
            int v=Q.front(); Q.pop();
            used[v]=0;
        }
        if (dfs(u)) res++;
        else break;
    }
    return res;
}
int main ()
{
    int n, u, v;
    n=Scan();
    init();
    FOR(i,1,n) {
        u=Scan(); v=Scan();
        uN=max(uN,u); uN=max(uN,v);
        addedge(u,i); addedge(v,i);
    }
    printf("%d\n",hungary());
    return 0;
}

这题的并查集做法还是神啊。。。

把一个有a,b两种属性的武器看成点a,b之间的无向边

对于一个联通块,假如不含环(就是一棵树),那么必定可以满足其中任意的p-1个点。

对于一个联通块,假如含环,那么必定全部的p个点都能满足。

那么合并并查集的时候可以利用一个vis来维护这个性质,把权值看成点,把武器看成边。如果每次加入的边是合并两个联通块

就把权值小的联通块并到权值大的联通块,然后给权值小的vis=true.如果不是,就把该联通块的顶点的vis=true

这样就可以保证,如果一个大小为N联通块1.=N-1条边构成,最大点的vis=false,其他为true

2.≥N条边构成,所有点的vis=true。

那么对于当前最小的i使得vis[i]=false.那么i-1一定是可以满足的最大数量。

简单证明:从小到大来看,对于没有关联边的点,显然vis[i]=false. 关联边的点,我们每次都可以选择一个联通块上的点,且这些点占用的边可以保持不同。

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi 3.1415926535
# define eps 1e-9
# define MOD 1000000009
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())==‘-‘) flag=1;
    else if(ch>=‘0‘&&ch<=‘9‘) res=ch-‘0‘;
    while((ch=getchar())>=‘0‘&&ch<=‘9‘)  res=res*10+(ch-‘0‘);
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar(‘-‘); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+‘0‘);
}
const int N=1000005;
//Code begin...

int fa[N];
bool vis[N];

int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
void un(int x, int y){
    if (x<y) swap(x,y);
    vis[y]=1; fa[y]=x;
}
int main ()
{
    int n, u, v, p, q;
    n=Scan();
    FOR(i,1,n+1) fa[i]=i;
    FOR(i,1,n) {
        u=Scan(); v=Scan();
        p=find(u), q=find(v);
        if (p==q) vis[p]=1;
        else un(p,q);
    }
    FOR(i,1,n+1) if (!vis[i]) {printf("%d\n",i-1); break;}
    return 0;
}

时间: 2024-12-26 17:42:22

BZOJ 1854 游戏(二分图匹配或并查集)的相关文章

bzoj 1059: [ZJOI2007]矩阵游戏 二分图匹配

1059: [ZJOI2007]矩阵游戏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1891  Solved: 919[Submit][Status] Description 小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏.矩阵游戏在一个N*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的).每次可以对该矩阵进行两种操作:行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)列交换操作:选择矩阵

[SCOI2010][BZOJ1854] 游戏|二分图匹配|匈牙利算法|并查集

1854: [Scoi2010]游戏 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 3018  Solved: 1099[Submit][Status][Discuss] Description lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并且每种装备最多只能使用一次. 游戏进行到最后,lxhgww遇到了终极boss

BZOJ 1854 游戏

SCOI省选题. 第一种做法是二分图匹配,即属性向装备连边,一旦不能匹配输出即可. 第二种做法是并查集.装备当作边连接两个点,如果某x条边没有环,它最大那个一定找不到属于自己的边.因此一旦某个点所在并查集中无环且最大的点是自己那么就可以输出答案了. 都很短. #include<iostream>#include<cstdio>#include<cstring>#include<cmath>#define maxl 5050#define maxr 20000

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

[传送门] (https://www.luogu.org/problemnew/show/P1640) Description lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并且每种装备最多只能使用一次.游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害.也就是说一开始

HDU1281-棋盘游戏-二分图匹配

先跑一个二分图匹配,然后一一删去匹配上的边,看能不能达到最大匹配数,不能这条边就是重要边 1 /*--------------------------------------------------------------------------------------*/ 2 3 #include <algorithm> 4 #include <iostream> 5 #include <cstring> 6 #include <ctype.h> 7 #i

HDU - 3081 Marriage Match II(二分图最大匹配 + 并查集)

题目大意:有n个男生n个女生,现在要求将所有的男女配对,所有的男女都配对的话,算该轮游戏结束了.接着另一轮游戏开始,还是男女配对,但是配对的男女不能是之前配对过的. 问这游戏能玩多少轮 男女能配对的条件如下 1.男女未曾吵架过. 2.如果两个女的是朋友,那么另一个女的男朋友可以和该女的配对 解题思路:并查集解决朋友之间的关系,找到能配对的所有情况 接着二分图匹配,找到一个完美匹配算玩了一轮游戏,接着将这完美匹配的边删掉,继续进行匹配 如果无法完美匹配了,表示游戏结束了 #include <cst

【bzoj1455】罗马游戏 可并堆+并查集

题目描述 罗马皇帝很喜欢玩杀人游戏. 他的军队里面有n个人,每个人都是一个独立的团.最近举行了一次平面几何测试,每个人都得到了一个分数. 皇帝很喜欢平面几何,他对那些得分很低的人嗤之以鼻.他决定玩这样一个游戏. 它可以发两种命令: 1. Merger(i, j).把i所在的团和j所在的团合并成一个团.如果i, j有一个人是死人,那么就忽略该命令. 2. Kill(i).把i所在的团里面得分最低的人杀死.如果i这个人已经死了,这条命令就忽略. 皇帝希望他每发布一条kill命令,下面的将军就把被杀的

CodeForces - 687D: Dividing Kingdom II (二分图&amp;带权并查集)

Long time ago, there was a great kingdom and it was being ruled by The Great Arya and Pari The Great. These two had some problems about the numbers they like, so they decided to divide the great kingdom between themselves. The great kingdom consisted

bzoj4025 二分图 [分治,并查集]

传送门 思路 是二分图的充要条件:图没有奇环. 考虑按时间分治,用可撤销并查集维护点到根的距离. 仍然可以用一个小trick把两点连边变成根连边,可以看这里. 每次连边时若不连通则连上,否则判一下有没有奇环.如果有输出"No",否则不用连. 我tm把T写成m狂WA不止 #include<bits/stdc++.h> namespace my_std{ using namespace std; #define pii pair<int,int> #define f