Crime
Time Limit: 2000/1000MS (Java/Others) Memory Limit: 262144/131072KB (Java/Others)
Problem Description
发生案件了。。。。Losanto的饭卡不见了。。。
经过观察,警方锁定了n个嫌疑人,打算进行集中审问。。。
只有两个审问的房间,而且使用时间有限,于是警方打算在两个房间里同时对多个嫌疑进行审问。现在警方得到了一张表,表示哪两个嫌疑人互相认识(认识没有传递性,比如A和B认识,B和C认识,不代表A和C认识),为了保证嫌疑人没有相互串通,要求每个房间里的嫌疑人互不认识。。
现在问题来了,警方不知道能不能在两个房间各一次审问中就完成对所有罪犯的审问。
如果可以分组,那么对一组中的一个人询问一个另一组中他认识的人的情况,而另一组中的那个人也会得到相应的这个人的有关问题。而每个人最多被询问一次也可以不询问,最多可以有多少次询问。
Input
多组测试数据,第一行是n和m,表示嫌疑人数目(1<n<=300),和m对认识关系m(0<m<=(n-1)*n/2)
接下来m行,每行有两个字符串a和b,表示名字为a和名字为b的嫌疑人认识(嫌疑人没有重名的,名字长度小于等于10个字母,不保证没有重复的认识关系)。
Output
可以完成分组则输出最多可以有多少次询问,不可以输出No。
Sample Input
4 4 Jack Tom
Jack David
Jack Lily
Tom David
6 5
Jack Tom
Jack David
Jack Lily
Tom Pete
David Alice
Sample Output
No
6
Source
第九届北京化工大学程序设计竞赛
Manager
老实说我觉得这道题的题意有点模糊不清,我们假设题意是想让我们求二分图的最大匹配,然后乘以2便是答案了。
我们需要先判断是否为二分图,用的是染色的方法。即给节点染两种颜色,相连的节点颜色不同,然后判断是否矛盾(其相邻节已染色点与其颜色是否相同)。
满足二分图条件后再用匈牙利算法求最大匹配就可以了。
刚开始一直WA,后来才发现搜增广路的时候写搓了,本来是若已有匹配,就判断从匹配的那点出发是否存在增广路,我却直接从这一点开始搜…以后要多注意细节。
还有一点,你搜最大匹配时集合的确定也决定了是否需要乘以2,这里不详细说了,有需要的可以看我博客总结匈牙利算法的文章。
下面是代码:
//Crime.cpp -- Acdream 1729
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 300 + 10;
const int maxm = 10 + 10;
char a[maxm], b[maxm];
map<string, int> mp;
int colour[maxn], match[maxn];
//vector<int> G[maxn];
int G[maxn][maxn];
bool used[maxn];
//bool fuck[maxn][maxn];
int n, m;
bool dfs_graph(int x, int y)
{
colour[x] = y;
for( int i=1; i<=n; i++ )
{
if( G[x][i] )
{
if( !colour[i] && !dfs_graph(i, -y)) return false;
else if( colour[i]==y ) return false;
}
}
return true;
}
bool dfs_match(int x)
{
for( int i=1; i<=n; i++ )
{
if( G[x][i] )
{
if( !used[i] )
{
used[i] = true;
if( !match[i] || dfs_match(match[i]) ) // 若k已有匹配,则从匹配k的点出发搜增广路
{
match[i] = x;
return true;
}
}
}
}
return false;
}
int hungary()
{
int ans = 0;
memset(match, 0, sizeof(match));
for( int i=1; i<=n; i++ )
{
if( colour[i]==1 ) // 每次都是从x集合向y集合连边
{ // 如果懒得分边的话就直接一直搜然后结果除以2
// 不过依然保证是从x向y连边,只不过有重复点而已
memset(used, 0, sizeof(used));
if( dfs_match(i) ) ans++;
}
}
return ans;
}
int main()
{
while( ~scanf("%d %d", &n, &m) )
{
bool flag = true;
memset(colour, 0, sizeof(colour));
memset(G, 0, sizeof(G));
//memset(fuck, 0, sizeof(fuck));
//for( int i=1; i<=n; i++ ) G[i].clear();
mp.clear();
for( int i=0, j=1; i<m; i++ )
{
scanf("%s %s", a, b);
string aa = a, bb = b;
if( !mp[aa] ) mp[aa] = j++;
if( !mp[bb] ) mp[bb] = j++;
/*
if( !fuck[mp[aa]][mp[bb]] )
{
G[mp[aa]].push_back(mp[bb]);
G[mp[bb]].push_back(mp[aa]);
fuck[mp[aa]][mp[bb]] = 1;
fuck[mp[bb]][mp[aa]] = 1;
}
*/
G[mp[aa]][mp[bb]] = G[mp[bb]][mp[aa]] = 1;
}
for( int i=1; i<=n; i++ )
{
if( !colour[i] )
{
if( !dfs_graph(i, 1) )
{
flag = false;
printf("No\n");
break;
}
}
}
if( flag )
{
int ans = hungary();
printf("%d\n", ans*2);
}
}
return 0;
}