洛谷 1525 关押罪犯
这题有两个做法:并查集和二分图匹配。
方案一:并查集
因为有矛盾值,所以显然需要排序:将矛盾值从小到大排序。
两个人a,b有仇,那么把他们放在一起显然会打起来,那么我们还不如把a与b的其他敌人放在一起,
因为这样可能会出现“敌人的敌人就是朋友”的情况,恰好a与b的其他敌人之间没有矛盾,那么他们就可以放在同一个集合中,反之b对a亦然。
那么我们不妨这样实现: 首先需要并查集初始化
(1)先把所有的矛盾关系按照矛盾值从大到小排一遍序,
(2)接下来每次操作取出一个关系,看矛盾的两个人x和y是否已经分配到同一个集合中(并查集找父亲即可),那么还分如下两种情况:
如果在一起那么显然会打起来(会出现矛盾),那么直接输出当前的边权(矛盾值)即可(此时保证是最小矛盾值,因为已经排序了)
如果不在同一组,则按照“敌人的敌人就是朋友”的原则,把x与y的其他敌人分在同一组,y与x的其他敌人分在同一组
不断进行以上操作最终可以得到答案
#include<bits/stdc++.h> using namespace std; const int N=2e4+10,M=1e5+10; int n,m,fa[N],d[N]; struct node { int a,b,c; }x[M]; bool cmp(node x,node y) { return x.c>y.c; } int get(int k) { if(fa[k]==k) return fa[k]; else return fa[k]=get(fa[k]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&x[i].a,&x[i].b,&x[i].c); for(int i=1;i<=n;i++) fa[i]=i; sort(x+1,x+1+m,cmp); for(int i=1;i<=m;i++) { int t1=get(x[i].a),t2=get(x[i].b); if(t1==t2) { printf("%d\n",x[i].c); return 0; } if(!d[x[i].a]) d[x[i].a]=x[i].b; else fa[get(d[x[i].a])]=get(x[i].b); if(!d[x[i].b]) d[x[i].b]=x[i].a; else fa[get(d[x[i].b])]=get(x[i].a); } puts("0"); return 0;
}
方案二:二分图判定
首先,我们考虑这样一个判定问题:是否存在一种分配方案,使得最小的矛盾值不超过mid。显然,当mid较小时可行的方案对于mid较大时依然可行。换言之,本题的答案具有单调性,可以采用二分的方法求解,将求最小值问题转换为判定问题
策略如下:我们二分答案,设当前二分的值为mid,此时任意两个矛盾双方x和y必须被分在两个不同集合中,将罪犯们作为节点,在矛盾值大于等于mid的罪犯之间连一条边,我们得到一张无向图。此时我们只需判定这张无向图是否为二分图即可(因为要分为两部分),如果是二分图,令二分右端点R=mid,否则令L=mid即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=1e9+7; struct Edge { int from,to,w; }p[200019]; int n,m,L,R,cnt,head[200019]; void add_edge(int x,int y,int W) { cnt++; p[cnt].from=head[x]; head[x]=cnt; p[cnt].to=y; p[cnt].w=W; } bool work(int mid) { queue <int> q; int color[20009]={0}; for(int i=1;i<=n;i++) if(!color[i]) { q.push(i); color[i]=1; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=head[x];i;i=p[i].from) if(p[i].w>=mid) { if(!color[p[i].to]) { q.push(p[i].to); if(color[x]==1) color[p[i].to]=2; else color[p[i].to]=1; } else if(color[p[i].to]==color[x]) return false; } } } return true;//正常结束,证明是二分图 } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y,w; scanf("%d%d%d",&x,&y,&w); R=max(R,w); add_edge(x,y,w); add_edge(y,x,w); } R++; while(R>L+1) { int mid=(L+R)/2; if(work(mid)) R=mid; else L=mid; } printf("%d",L); return 0; }
浙公网安备 33010602011771号