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

 

posted @ 2019-10-28 07:27  逐梦——无畏  阅读(197)  评论(0)    收藏  举报