NOIp 2010/Luogu P1525 关押罪犯 【二分图/并查集】 By cellur925

题目传送门

感想:相信自己的想法!继续挖掘!

读完题目后:看到的最大值最小?二分答案啊!再仔细一看:wi达到了1e9,二分可能费点劲。(其实真的是可以的)而且check函数貌似并没有什么行之有效的写法。继续往下想。

再读读,想到我们肯定尽量不想让有仇恨的犯人关在一起,所以每次就把有仇的敌人用并查集并在一起(其实想法是挺好的,到这一步出现了偏差)。

结果...然后就没有结果了!

唉其实应该自己再多想想的嘛...还是去看了@Chemist和@new2zy两位巨佬的题解。主要有两种方法。

 

法一:二分图+二分答案

这题很好的满足了二分图的性质。是不错的二分图例题。

给出二分图定义:若无向图的n个节点可分为A,B两个非空集合,且A,B的交集为空集,且同一集合内的点没有边相连,称这种图为二分图。

二分图判定定理:一张无向图是二分图,当且仅当它不存在奇环。

代码实现:染色法。用黑白来标记图中两种节点,用大法师(Dfs)或绑发饰(Bfs)实现,遍历每条边如果没被访问,就染相反的颜色,否则判断它已染的色是否不合法。(根据定义,一个节点被标记后,它所有的相邻节点颜色应与他不同)。

get了这个知识,我们可以与最初的想法二分答案结合,本题中一个合法解需满足存在二分图的情况,而最优解可以通过二分实现。那么判断是否为二分图就成为了check函数的内核。

Code

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<queue>
 5 
 6 using namespace std;
 7 
 8 int n,m,tot,l,r;
 9 int head[20090],vis[20090];
10 struct node{
11     int to,next,val;
12 }edge[200090];
13 
14 void add(int x,int y,int z)
15 {
16     edge[++tot].to=y;
17     edge[tot].val=z;
18     edge[tot].next=head[x];
19     head[x]=tot;
20 }
21 
22 bool check(int w)
23 {
24     memset(vis,0,sizeof(vis));
25     queue<int>q;
26     for(int k=1;k<=n;k++)
27         if(!vis[k])
28         {
29             q.push(k);vis[k]=1;
30             while(!q.empty())
31             {
32                 int x=q.front();
33                 q.pop();
34                 for(int i=head[x];i;i=edge[i].next)
35                     if(edge[i].val>=w)
36                     {
37                         int y=edge[i].to;
38                         if(!vis[y]) vis[y]=3-vis[x],q.push(y);
39                         else if(vis[y]==vis[x]) return false;
40                     }
41             }
42         }
43     return true;
44 }
45 
46 int main()
47 {
48     scanf("%d%d",&n,&m);
49 //    l=1;
50     for(int i=1;i<=m;i++)
51     {
52         int a=0,b=0,c=0;
53         scanf("%d%d%d",&a,&b,&c);
54         add(a,b,c);
55         add(b,a,c);
56         r=max(r,c);
57     }//r++;
58     while(l<r)
59     {
60         int mid=(l+r+1)>>1;
61         if(check(mid)) r=mid-1;
62         else l=mid;
63     }
64     printf("%d",l);
65     return 0;
66 }
View Code

*代码细节注意:入队地点(没被访问过),二分细节。

 

法二:冰茶几+略微贪心思想

由于Chemist大神已经讲的十分清楚,我就少发表些拙见。(扔下地址跑)

我们当然可以贪心地把仇恨值从大到小排序,先使仇恨值大的几对罪犯分到两个监狱,再秉承《团伙》一题中“敌人的敌人就是朋友”的观念,如果实在分不开了,当前的就是答案。

 

现在这么优质的一题多解的noip题已经很难找了。好题!好题!

posted @ 2018-08-16 23:11  cellur925&Chemist  阅读(160)  评论(0编辑  收藏  举报