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; }