洛谷-關押罪犯-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 }
相關文章
相關標籤/搜索