题解 P1525 【关押罪犯】
蒟蒻看到这题其实思考了一些问题才开始编程,这里讲一下问题并且放出代码
为什么贪心是正确的
首先看到这道题大家都会想到把冲突值从大到小排序,然后从大到下进行加边,为什么之后加入的不会影响前面的(这里唯一的变换方式只有在两个监狱中变换【起码我一开始是这么想的】)
但后来想到其实不用进行变换,因为有一个很重要的性质叫 仇人的仇人就是朋友同时,只有两座监狱,蒟蒻灵光一闪!
这样问题一下子就变得很简单了,对于新加入的边,进行某些亦敌亦友的操作后一定就可以轻松解决上面那个问题了(这个问题会在下面详细说到)
如何实现
除了一般的并茶几数组,还有维护一个东西叫仇人数组的东西 这个仇人数组怎么处理呢?我们使用如下方法
对于还没有敌人,而被处理到的罪犯,我们更新仇人为另一个罪犯
对于已经有敌人的罪犯,直接把和他有冲突的罪犯和仇人合并
正确性好像很显然
想到这些,这道题目就已经解决了
代码
一点注意:对于完美情况要输出0
#include <bits/stdc++.h>
using namespace std;
const int maxn=100005;
struct edge{
int u,v,w;
}e[maxn];
int fa[maxn<<1],dr[maxn<<1];
int n,m;
bool cmp(edge x,edge y){
return x.w>y.w;
}
inline int read(){
int f=1,x=0;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
inline int find(int x){
if(fa[x]==x)return x;
fa[x]=find(fa[x]);
return fa[x];
}
bool judge(int x,int y){
if(find(x)==find(y))return 1;
else return 0;
}
inline void merge(int x,int y){
fa[find(x)]=find(y);
}
int main(){
memset(dr,0,sizeof(dr));
n=read();m=read();
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=1;i<=m;i++){
e[i].u=read(),e[i].v=read(),e[i].w=read();
}
int tag=0;
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++){
if(judge(e[i].u,e[i].v)){
cout<<e[i].w;
tag=1;
break;
}
else{
if(!dr[e[i].u]){
dr[e[i].u]=e[i].v;
}
else{
merge(dr[e[i].u],e[i].v);
}
if(!dr[e[i].v]){
dr[e[i].v]=e[i].u;
}
else{
merge(dr[e[i].v],e[i].u);
}
}
}
if(!tag)cout<<"0";
return 0;
}
代码中使用了若干中文括号,直接复制将会ce;