关押罪犯 Page423 并查集/二分图判定
一、并查集
思路:贪心思想,将怒气值从大到小排序,尽可能把怒气值大的放在不同的监狱,即不同的集合内,一共两个监狱,放在不同的监狱不能表示为unite(a,b),但是可以表示为unite(a,b+n),unite(a+n,b)表示a和b的敌人在一个监狱中,b和a的敌人在一个监狱中,当两人属于同一监狱时直接输出
代码:
int fa[maxn],n,m;
struct node
{
int x,y,z;
}p[maxn];
bool operator<(node a,node b){return a.z>b.z;}
int find(int x)
{
if (fa[x]==x)return x;
else return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if (x!=y)fa[x]=y;
}
int main()
{
scanf("%d%d",&n,&m);
rep(i,1,m)scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
rep(i,1,2*n)fa[i]=i;
sort(p+1,p+1+m);
rep(i,1,m)
{
if (find(p[i].x)==find(p[i].y))
{
W(p[i].z);
return 0;
}
unite(p[i].x,p[i].y+n);
unite(p[i].y,p[i].x+n);
}
W(0);
return 0;
}
二、二分图判定
思路:这个答案具有单调性,如果最大的怒气值为a,那么最大的怒气值为a+1的时候也显然满足,所以需要找到该最小的最大怒气值可以采用二分,将不大于mid的边都加入,配合二分图判定的复杂度是 O ( 2 N + M ) ∗ l o g m O(2N+M)*logm O(2N+M)∗logm
代码:
int n,m,head[maxn],tot=0,b[maxn],vis[maxn];
bool mark;
struct node
{
int x,y,z;
}p[maxn];
struct
{
int to,next,val;
}edge[maxn];
bool operator<(node a,node b){return a.z<b.z;}
void add(int u,int v,int w){edge[++tot].to=v;edge[tot].next=head[u];edge[tot].val=w;head[u]=tot;}
void dfs(int x,int val,int K)
{
b[x]=val;
vis[x]=1;
for (int i=head[x];i!=-1;i=edge[i].next)
{
int y=edge[i].to;
if (edge[i].val<=K)continue;
if (!vis[y])dfs(y,3-val,K);
else if (b[y]==val)mark=false;
}
}
bool work(int K)
{
mark=true;
rep(i,1,n)vis[i]=b[i]=0;
rep(i,1,n)if (!vis[i])dfs(i,1,K);
return mark;
}
int main()
{
scanf("%d%d",&n,&m);
mem(head,-1);
rep(i,1,m)
{
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
add(p[i].x,p[i].y,p[i].z);
add(p[i].y,p[i].x,p[i].z);
}
sort(p+1,p+1+m);
int l=0,r=m;
while(l<r)
{
int mid=(l+r)>>1;
if (work(p[mid].z))r=mid;
else l=mid+1;
}
W(p[l].z);
return 0;
}