2017. Best of a bad lot
Memory limit: 64 MB
- Where were you at the moment of departure?
- Who did you see there?
Input
Output
Samples
input | output |
---|---|
3 bar 0 bar 1 1 pool 2 1 2 | 1 3 |
3 pool 2 2 3 pool 2 1 3 pool 2 1 2 | 1 1 |
Notes
Problem Source: NEERC 2014, Eastern subregional contest
好久没有系统的弄图论,今天遇到这要一道二分图的题捡了好久……
大致题意:一艘邮轮上除了杀人案,然后让你断案,对于每一个船上的人,我们问两个问题,第一是当时他在哪,以及在那个地方看见了谁。然后只要两个人有矛盾,那么我们就认为这两个人中一定有一个人是其中一个嫌疑人。现在让你确定一个最小的范围,使得嫌疑人的数量尽可能少,并输出任意一种嫌疑人方案。这里特别说明,嫌疑人知道你会这么判断,所以嫌疑人之间会统一口供,然后好人也没有必要去撒谎。
首先,我们要确定如何判定两个人是否有矛盾。一开始,我认为,如果A自己说的所在地点与B陈述的A的所在地点不同,那么A与B有矛盾。这样判断是正确的,但是会漏掉一些情况。对于两个点u和v,如果两个人不相互陈述对方的地点,我们不能因此断定他俩没有矛盾,因为如果u和v的地点不同,然后u和v都宣称看到了x,那么u和v也是矛盾的。故正确的判断方法是,首先把每个人看到的人的列表中加上自己,然后枚举任意不在统一地点的两个人,看他俩是否有宣称看到同一个人,如果有那就有矛盾,否则没有。
判断矛盾之后,很自然而然的,我们会想到连接有矛盾的边,表示该边的两端至少一个是嫌疑人。如此之后,相当于我们要在这个图中找一个最大独立集,于是开始往二分图匹配方向上思考,但是匹配无法解决方案数的输出问题(对于一个匹配,我们并不知道确定哪一个人为罪犯)。于是得换一种想法,二分图Emmm……我们这时再认真读一下题目,我们就会注意到,罪犯之间统一口供,然后好人也没必要撒谎。这就意味着,如果a与b有矛盾,b与c有矛盾,那么a与c一定没有矛盾,即图具有“反传递性”。为什么呢?假设b是嫌疑人,那么a与c不是嫌疑人而是好人,而好人没必要撒谎,所以a与c一定没有矛盾;假设b不是嫌疑人,那么a和c都是嫌疑人,由于嫌疑人统一了口供,所以说嫌疑人之间也不会存在矛盾,那么a与c也一定没有矛盾。故在以上条件下a与c没有矛盾,也就意味着这个原图可以直接转换成一个二分图。
于是我们就可以把点分成两类,一类为好人,一类为嫌疑犯。既然可以人为的分组,那么我们就可以贪心的解决这个问题。由于这个矛盾关系不一定保证是一个连通图,所以说可以看作是很多个二分子图。对于每一个子图,我们可以用交叉染色法分成两组,而这两组具体哪个定义为好人,哪个为嫌疑人,也是由我们自己决定的,所以我们当然是把人多的那一组定为好人,少的定为嫌疑人。对所有子图这么做即可解出此题。
然后,再总结一些二分图的具体知识。对于二分图的判定,我们就可以用我生造出来的“反传递性”来判定,编程实现的话可以用交叉染色法。具体分配也是用这个方法。然后此题看似是用了交叉染色法解决了最大独立集的问题,但是实际上是不能的。特殊之处在于本题的图不是固定的,我可以自由的分配两部分中哪一部分是在左边,哪一部分是在右边的。真正要求最大独立集,还是要用n-最大匹配数。具体见代码:
#include<bits/stdc++.h>
#define N 410
using namespace std;
struct node
{
string place;
bool v[N];
} p[N];
vector<int> g[N],res[2],ans;
bool v[N]; int n;
void dfs(int x,int wh)
{
v[x]=1;
res[wh].push_back(x);
for(int i=0;i<g[x].size();i++)
{
int y=g[x][i];
if (!v[y]) dfs(y,wh^1);
}
}
int main()
{
while (~scanf("%d", &n))
{
ans.clear();
memset(v,0,sizeof(v));
memset(g,0,sizeof(g));
for(int i=1;i<=n;i++)
{
int x,y;
cin>>p[i].place;
scanf("%d",&x); p[i].v[i]=1;
while(x--)
{
scanf("%d",&y);
p[i].v[y]=1;
}
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
if (p[i].place==p[j].place) continue;
for(int k=1;k<=n;k++)
if (p[i].v[k]&&p[j].v[k])
{
g[i].push_back(j);
g[j].push_back(i);
}
}
for(int i=1;i<=n;i++)
if (!v[i])
{
memset(res,0,sizeof(res));dfs(i,0);
int wh=res[0].size()>res[1].size();
for(int j=0;j<res[wh].size();j++)
ans.push_back(res[wh][j]);
}
if (!ans.size()) ans.push_back(1);
printf("%d\n",(int)ans.size());
for(int i=0;i<ans.size();i++)
printf("%d ",ans[i]);
puts("");
}
return 0;
}