洛谷-关押罪犯-NOIP2010提升组复赛

题目描述

S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N。他们之间的关系天然也极不和谐。不少罪犯之间甚至积怨已久,若是客观条件具有则随时可能爆发冲突。咱们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。若是两名怨气值为c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并形成影响力为c 的冲突事件。ios

每一年年底,警察局会将本年内监狱中的全部冲突事件按影响力从大到小排成一个列表,而后上报到S 城Z 市长那里。公务繁忙的Z 市长只会去看列表中的第一个事件的影响力,若是影响很坏,他就会考虑撤换警察局长。数组

在详细考察了N 名罪犯间的矛盾关系后,警察局长以为压力巨大。他准备将罪犯们在两座监狱内从新分配,以求产生的冲突事件影响力都较小,从而保住本身的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们必定会在每一年的某个时候发生摩擦。函数

那么,应如何分配罪犯,才能使Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?优化

输入输出格式

输入格式:spa

输入文件的每行中两个数之间用一个空格隔开。第一行为两个正整数N 和M,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的M 行每行为三个正整数aj,bj,cj,表示aj 号和bj 号罪犯之间存在仇恨,其怨气值为cj。数据保证1<aj=<=bj<=N ,0 < cj≤ 1,000,000,000,且每对罪犯组合只出现一次。.net

输出格式:code

共1 行,为Z 市长看到的那个冲突事件的影响力。若是本年内监狱中未发生任何冲突事件,请输出0。blog

输入输出样例

输入样例#1:
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
输出样例#1:
3512

说明

【输入输出样例说明】罪犯之间的怨气值以下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件影响力是3512(由2 号和3 号罪犯引起)。其余任何分法都不会比这个分法更优。排序

【数据范围】对于30%的数据有N≤ 15。对于70%的数据有N≤ 2000,M≤ 50000。对于100%的数据有N≤ 20000,M≤ 100000。事件

 

思路1:

考虑用二分答案+二分图判断

咱们不难想到,a与b有c这么多的矛盾,则能够说a与b间有权重为c的边,这样就构成了有n个顶点m条边的无向图。

将罪犯分配到两个监狱中,不难想到是二分图。

排序罪犯的怒气值c,进行二分查找,对于当前找到的这个怒气值(边)mid,咱们将图中比这条mid边权重小或等于的边暂时删去,判断剩下的图可否构成一个二分图,若是构成则当前的这个怒气值mid即为所求,输出结束程序便可,不要忘了若是没有任何冲突事件发生则输出0.

拓展:二分图判断——染色法

从其中一个顶点开始,将跟它邻接的点染成与其不一样的颜色,若是邻接的点有相同颜色的,则说明不是二分图,每次用bfs遍历便可

判断代码以下(源自:https://blog.csdn.net/zhangxian___/article/details/73699241):

 1 #include <queue>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 
 6 const int N=510;
 7 int color[N],graph[N][N];
 8 
 9 //0为白色,1为黑色
10 bool bfs(int s, int n)
11 {
12     queue<int> q;
13     q.push(s);
14     color[s]=1;
15     while(!q.empty())
16     {
17         int from=q.front();
18         q.pop();
19         for(int i=1;i<=n;i++)
20         {
21             if(graph[from][i]&&color[i]==-1)
22             {
23                 q.push(i);
24                 color[i]=!color[from];//染成不一样的颜色
25             }
26             if(graph[from][i]&&color[from]==color[i]) return false;//颜色有相同,则不是二分图
27         }
28     }
29     return true;
30 }
31 
32 int main() 
33 {  
34     int n,m,a,b,i;
35     memset(color,-1,sizeof(color));
36     cin>>n>>m;
37     for(i=0;i<m;i++)
38     {
39         cin>>a>>b;
40         graph[a][b]=graph[b][a]=1;
41     }
42     bool flag=false;
43     for(i=1;i<=n;i++)
44         if(color[i]==-1&&!bfs(i,n)) //遍历各个连通分支
45         {
46             flag=true;
47             break;
48         }  
49     if(flag)
50         cout<<"NO"<<endl;
51     else
52         cout<<"YES"<<endl;
53     return 0;
54 }

 

思路2:

考虑用并查集+贪心思想

这个方法较上面的方法容易理解,贪心地:咱们但愿怒气值很大的两个罪犯不在同一个监狱,如遇到两个罪犯不得不在一个监狱时,这时候输出的结果即为最大值。

咱们按照并查集路径优化的思想,两个监狱各选出一个头子,这样咱们在判断两个罪犯是否要放在两个监狱时,只有看看他们以前是否就归附于同一个头子,若是以前两人都归附于同一个监狱的头子,输出就OK,不然安排他俩进两个监狱。

按照怒气值从大到小依次取出两个罪犯,看看他们可否放在两个监狱:

若是能放,必需要知足不归附于同一个头子。

若是不能放,则安排他俩进进不一样监狱,而后两个罪犯分别当上两个监狱的头子(即让原来的监狱头子归附于当前a与b)。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <math.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 using namespace std;
 7 typedef struct conflict//存储矛盾信息 
 8 {
 9     int a,b,c;//a与b有c这么多的矛盾(怒气) 
10 }conflict;
11 
12 int against[20002]={0};//against[i]存储i的敌人 
13 int father[20002];//记录这个节点的父亲 
14 conflict infermation[100002];//记录信息数组 
15 
16 bool cmp(conflict x,conflict y)//排序函数 
17 {
18     return x.c>y.c;
19 }
20 
21 int find(int x)//寻找x节点的头子(并查集+路径压缩) 
22 {
23     if(x!=father[x]) father[x]=find(father[x]);
24     return father[x];
25 }
26 
27 int main()
28 {
29     int n,m;
30     int i,j;
31     int x,y;//x为a的祖先,y为b的祖先 
32     //freopen("prison.in","r",stdin);
33     //freopen("prison.out","w",stdout);
34     scanf("%d%d",&n,&m);
35     for(i=1;i<=m;i++)//输入信息 
36     {
37         scanf("%d%d%d",&infermation[i].a,&infermation[i].b,&infermation[i].c);
38     }
39     sort(infermation+1,infermation+m+1,cmp);//按矛盾值从大到小排序结构体 
40     for(i=1;i<=n;i++)
41     {
42         father[i]=i;//并查集初始化(本身是本身的父亲) 
43     }
44     for(i=1;i<=m;i++)//从大到小取出矛盾值判断 
45     {
46         if(find(infermation[i].a)==find(infermation[i].b))//若是两个罪犯已经在一个监狱了(他们都归附于同一个头子),确定是最优值,输出,结束程序 
47         {
48             printf("%d\n",infermation[i].c);
49             return 0;//直接结束程序 
50         } 
51         if(!against[infermation[i].a])//若是a没有敌人 
52         {
53             against[infermation[i].a]=infermation[i].b;//那么b纳入a的敌人中 
54         }
55         else
56         {
57             father[find(against[infermation[i].a])]=father[infermation[i].b];//不然把b和a的敌人分在一块儿,即a的敌人头子指向b的父亲 
58         }
59         if(!against[infermation[i].b])//若是b没有敌人 
60         {
61             against[infermation[i].b]=infermation[i].a; //那么a纳入b的敌人中 
62         }        
63         else
64         {
65             father[find(against[infermation[i].b])]=father[infermation[i].a];//不然把a和b的敌人分在一块儿,即b的敌人头子指向a的父亲 
66         }    
67     }
68     printf("0\n");//没有冲突找到则输出0 
69     return 0;
70 }
相关文章
相关标签/搜索