转:洛谷 P1525 关押罪犯 并查集实现 和 二分图实现

https://www.luogu.org/blog/new2zy/solution-p1525

1.并查集

本题,因为说了有“边权值”(我理解为矛盾值),所以要求出现矛盾情况下的最小边权值显然是需要排序的

那么问题又来了,我们要按照什么方法进行分配呢?

我们不妨这样想:两个人a,b有仇,那么把他们放在一起显然会打起来,那么我们还不如把a与b的其他敌人放在一起,

因为这样可能会出现“敌人的敌人就是朋友”的情况,恰好a与b的其他敌人之间没有矛盾,那么他们就可以放在同一个集合中,反之b对a亦然。

那么我们不妨这样实现: 首先需要并查集初始化

(1)先把所有的矛盾关系按照矛盾值从大到小排一遍序,

(2)接下来每次操作取出一个关系,看矛盾的两个人x和y是否已经分配到同一个集合中(并查集找父亲即可),那么还分如下两种情况:

如果在一起那么显然会打起来(会出现矛盾),那么直接输出当前的边权(矛盾值)即可(此时保证是最小矛盾值,因为已经排序了)

如果不在同一组,则按照“敌人的敌人就是朋友”的原则,把x与y的其他敌人分在同一组,y与x的其他敌人分在同一组

不断进行以上操作最终可以得到答案。

伪码如下:

struct Edge//定义边:起点from,终点to,边权w 每条边表示的是敌人关系,边权代表怨气值 
{
    int from,to,w;
}E[maxm];
int n,m,fa[maxn],Enemy[maxn];//fa[i]是i的父亲(fa是并查集),Enemy[i]是i的第一个敌人。
//一般的 并查集应用中,边所表达的关系就是朋友,朋友的朋友是朋友,关系的传递性很直接 
//而本题边所表达的关系是敌人,而是敌人的敌人是朋友,关系的传递性是间接的,所以必须 用另一个数组存一个敌人,起一个桥梁作用。 

int main()
{
    n=read();m=read();
    
    for(int i=1;i<=m;i++){//加边 起点from,终点to,边权w 每条边表示的是敌人关系,边权代表怨气值 
        读边信息 
    } 
    并查集初始化。有人初始化为0,有人初始化为i,都可以。只是在找祖先时,条件不同 
     
    sort(E+1,E+1+m,cmp);//按怒气值从大到小排序,下面的代码就是尽量将怨气值大的两个敌人分在不同集合。
     
    for(int i=1;i<=m;i++){
       
        从E[i]数组中读出一条边信息 u,v,w 
        如果u,v在一个集合中,即两个敌人出现在一个集合中,则出现矛盾,直接结束 ,uv的怨气值即为所求。退出程序。 
      
        如果u,v不在一个集合中,把敌人的敌人与自己分到一个集合中,具体如下:
        //    处理u 
            Enemy的初始值为0,如果还没有找到 u 的敌人,则v是u的第一个敌人,即 Enemy[u]=v;
               如果 u 已经有第一个敌人,则u 的敌人就是v的朋友,即合并 u的敌人和v, 即merge(Enemy[u],v ) 
        同上 处理v 
           
    }
    printf("0\n");//没有矛盾
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值