关押罪犯 - C++

关押罪犯

题目描述
S 城现有两座监狱,一共关押着 N 名罪犯,编号分别为 1−N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为 c的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 c 的冲突事件。每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 S 城 Z 市长那里。公务繁忙的 Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。在详细考察了N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?
输入格式
每行中两个数之间用一个空格隔开。第一行为两个正整数 N,M,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的 M 行每行为三个正整数 aj,bj,cj,表示 aj号和 bj号罪犯之间存在仇恨,其怨气值为 cj​。数据保证 1<aj≤bj≤N,0<cj≤10e9,且每对罪犯组合只出现一次。
输出格式
共 1 行,为 Z 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出 0。
输入输出样例输入 #1
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
输出 #1
3512

这道题开始时我想的时二分图。但是打开题解时发现大多数是并查集解法。看了并查集解法发现有些难以理解。就是一种敌人的敌人便是朋友思想来解决这道题。首先是对所有的敌人之间仇恨值进行排序。 再用数组记录敌人节点。然后每次把敌人的敌人合并。
代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath> 
using namespace std;
int n,m;
int f[20001];
int a[20001];
struct edge{
  int u,v,w; 
}e[100001];
bool compare(edge u, edge v)
{
  return u.w > v.w;
}
int getf(int u)
{
    if(u != f[u])
    {
     f[u] = getf(f[u]);
     return f[u];
    }
    return u;
}
void merge(int u, int v)
{
   int t1,t2;
   t1 = getf(u),t2 = getf(v);
   if(t1 != t2)
   f[t2] = t1;
   return;
}
bool check(int u, int v)
{
   if(getf(u) == getf(v))
   return true;
   return false;
}
int main()
{
   cin >> n >> m;
   
   for(int i = 1; i <= n; i ++)
   f[i] = i;
   for(int i = 1; i <= m; i ++)
   scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
   sort(e + 1, e + m + 1,compare);
   for(int i = 1; i <= m + 1; i ++)
   {
    if(check(e[i].u , e[i].v))
    {
       printf("%d",e[i].w);
  break; 
 }
    else
    {
       if(!a[e[i].u])
  a[e[i].u] = e[i].v;
    else
       merge(a[e[i].u],e[i].v);
    if(!a[e[i].v])
       a[e[i].v] = e[i].u;
    else
       merge(e[i].u,a[e[i].v]); 
 }
   }
   return 0;
}

然后又用了二分图的解法发现也可行,将仇恨值最大值找出。在从0到这个值二分法,再根据两个敌人之间仇恨值是否大于该值进行染色,如果整个图之间没有冲突,返回true,否则返回false。直到找到这个仇恨值的最小值。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int n,m,cnt;
int head[20001];
struct edge{
   int t,next,w;
}e[200002];
int color[20001];
void add(int u, int v, int w)
{
   cnt ++;
   e[cnt].t = v;
   e[cnt].next = head[u];
   head[u] = cnt;
   e[cnt].w = w; 
}
bool bfs(int mid)
{
    queue<int> q;
    int color[20001] = {0}; 
    for(int i = 1; i <= n; i ++)
    {
         if(!color[i])
         {
           color[i] = 1; 
            q.push(i);
       while(q.size())
      {
         int k = q.front();
    q.pop();
    for(int i = head[k]; i != 0; i = e[i].next)
    {
       if(e[i].w >= mid)
       {
           if(!color[e[i].t])
      {
         q.push(e[i].t);
         if(color[k] == 1)
         color[e[i].t] = 2;
         else
         color[e[i].t] = 1; 
      }
      else
      if(color[e[i].t] == color[k])
      return false; 
       }  
    } 
      } 
     }
    } 
    return true;
}
int main()
{
  int R = 0,L = 0; 
  cin >> n >> m;
  for(int i = 1; i <= m; i ++)
  {
   int u,v,w;
   scanf("%d%d%d",&u,&v,&w);
   add(u,v,w);
   add(v,u,w); 
   R = max(R,w);
  }
  while(L < R - 1)
  {
   int mid = (L + R) / 2;
      if(bfs(mid))
  R = mid;
 else
  L = mid; 
  //cout << L << " " << R;
  }
  cout << L;
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值