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