关押罪犯
Description
S 城现有两座监狱,一共关押着N名罪犯,编号分别为1~N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为c的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为c的冲突事件。
每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到S 城Z 市长那里。公务繁忙的Z市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。
在详细考察了N名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。那么,应如何分配罪犯,才能使Z市长看到的那个冲突事件的影响力最小?这个最小值是多少?
Analysis
对手并查集
首先想到将罪犯仇恨度由大到小排序,枚举可以控制的罪犯关系,直到发现此关系无法控制,则直接输出该仇恨度,后面的关系都没必要处理。
而判断隔离罪犯的可行性不难想到用并查集判断矛盾性,如果需要隔离的罪犯已经关到一起自然无法控制。这里要使用对手并查集,只要数组开到2倍,令i+n为i的对手集合即可。
Code
#include <bits/stdc++.h>
#define ll long long
int n,m,fa[200010];
struct node
{
int b,e;
ll s;
}edge[100010];
int find(int x){
if(x==fa[x])
return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
fa[find(x)]=find(y);
}
bool cmp(node p, node q)
{
if(p.s==q.s)
return p.b>q.b;
return p.s>q.s;
}
void operate()
{
for(int i=1;i<=m;i++)
{
int b=edge[i].b,e=edge[i].e;
if(find(b)==find(e)){
std::cout<<edge[i].s<<std::endl;
return;
}
merge(b,e+n);
merge(e,b+n);
}
std::cout<<"0"<<std::endl;
return ;
}
int main()
{
freopen("prison.in","r",stdin);
freopen("prison.ans","w",stdout);
std::cin>>n>>m;
for(int i=1;i<=m;i++)
std::cin>>edge[i].b>>edge[i].e>>edge[i].s;
std::sort(edge+1,edge+m+1,cmp);
for(int i=1;i<=2*n;i++)
fa[i]=i;
operate();
return 0;
}
更新于 2018/8/16
二分图匹配
和对手并查集类似的思路,不过效率要低一些。
不难发现罪犯之间的关系可以描述为一个无向有环图,当出现奇数节点组成的环时无法妥善处理。
所以可以枚举最小冲突值,将必须隔离的罪犯组合建图染色,如果出现组合内罪犯颜色相同则需枚举更大的冲突值,不出现则此冲突值可行。为了加速程序处理,不难想到二分答案。
Code
#include <bits/stdc++.h>
int n,m,col[20010];
class Chain_forward_star{
private:
int tot,head[20010];
class chain{
public:
int v,s,next;
}edge[200010];
public:
void init(int u,int v,int s){
edge[++tot].v=v;
edge[tot].s=s;
edge[tot].next=head[u];
head[u]=tot;
}
bool dye(int i,int c,int lim){
for(i=head[i];i;i=edge[i].next)
if(edge[i].s>lim){
int v=edge[i].v;
if(col[v]==c)return 1;
if(col[v]==3-c)continue;
col[v]=3-c;
if(dye(v,3-c,lim))return 1;
}
return 0;
}
}edge;
int search(int l,int r){
if(l==r-1)return r;
int mid=(l+r)>>1;
memset(col,0,sizeof(col));
for(int i=1;i<=n;i++)
if(!col[i]&&edge.dye(i,1,mid))
return search(mid,r);
return search(l,mid);
}
int main(){
freopen("prison.in","r",stdin);
freopen("prison.ans","w",stdout);
std::cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,s;
std::cin>>u>>v>>s;
edge.init(u,v,s);
edge.init(v,u,s);
}
std::cout<<search(-1,1000000000)<<std::endl;
return 0;
}