题意:
给你m对矛盾关系,每对关系分别涉及到x,y两人,矛盾值为w
请你判断分配x和y到两个集合中,能否避免冲突
如能避免请输出0,如果冲突不可避免,请输出最小的矛盾值
思路:
方法①:并查集
并查集能维护连通性、传递性,通俗地说,亲戚的亲戚是亲戚
。
我们不妨这样想:两个人a,b有仇,那么把他们放在一起显然会打起来,那么我们还不如把a与b的其他敌人放在一起,
因为这样可能会出现“敌人的敌人就是朋友”的情况,恰好a与b的其他敌人之间没有矛盾,那么他们就可以放在同一个集合中,反之b对a亦然
我们可以将没对关系按照权值从大到小排序,这样可以保证一旦发生冲突,答案是最小的
对于加入的每段关系,我们先判断他们是否在同一集合内,如果在的话就说明发生冲突,直接输出答案
如果不在一个集合内,我们就将敌人的敌人加入到自己的集合中
#include#include #include #include using namespace std; const int maxn1=2e4+10; const int maxn2=1e5+10; int fa[maxn1],ene[maxn1]; struct node{ int u,v,w; }edge[maxn2]; int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));} int cmp(node a,node b){return a.w>b.w;} int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w); for(int i=1;i<=n;i++) fa[i]=i; memset(ene,0,sizeof(ene)); sort(edge+1,edge+1+m,cmp); for(int i=1;i<=m;i++){ int u=edge[i].u,v=edge[i].v,w=edge[i].w; int f1=find(u),f2=find(v); if(f1==f2){ cout< endl; return 0; } else{ if(!ene[u]) ene[u]=v; else fa[find(ene[u])]=find(v); if(!ene[v]) ene[v]=u; else fa[find(ene[v])]=find(u); } } cout<<0<<endl; return 0; }
方法②:二分图
看到将犯人分成两批应该很容易想到二分图的做法
那么很明显,不是所有所有情况下都能讲犯人分成两部分,必定有一些冲突是无法避免的
我们也可以注意到,冲突是具有单调性的,我们就可以想到二分的做法
我们二分答案,设当前二分的值为mid,此时任意两个矛盾双方x和y必须被分在两个不同集合中,将罪犯们作为节点,在矛盾值大于等于mid的罪犯之间连一条边,我们得到一张无向图。此时我们只需判定这张无向图是否为二分图即可(因为要分为两部分),如果是二分图,令二分右端点R=mid,否则令L=mid即可
#include#include #include #include #include using namespace std; const int maxn=2e5+10; struct edge{ int u,v,w; }p[maxn]; int n,m,L=0,R=0,cnt=0,head[maxn]; void add_edge(int x,int y,int w) { p[++cnt].u=head[x]; head[x]=cnt; p[cnt].v=y; p[cnt].w=w; } bool judge(int mid) { queue<int> q; int color[maxn]={0}; for(int i=1;i<=n;i++){ if(!color[i]) color[i]=1,q.push(i); while(!q.empty()){ int x=q.front(); q.pop(); for(int j=head[x];j;j=p[j].u){ if(p[j].w>=mid){ if(!color[p[j].v]){ q.push(p[j].v); if(color[x]==1) color[p[j].v]=2; else color[p[j].v]=1; } else if(color[p[j].v]==color[x]) return false; } } } } return true; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); R=max(R,w); add_edge(u,v,w); add_edge(v,u,w); } int ans=0; R++; while(L+1<R){ int mid=(L+R)>>1; if(judge(mid)) R=mid; else L=mid; } cout< endl; return 0; }