POJ 2289 Jamie's Contact Groups 多重匹配+二分
2012-08-05 18:18
423 查看
题目大意是给你一堆联系人, 每个人有几个标签, 然后让你将他们分组(每组里的人要标签相同), 问其中人最多的组的人数最少是多少.
有点像鸽巢的感觉...开始我想了一个, 以为是每次求最大匹配, 然后把已盖点的邻边全删掉, 然后看能求几次最大匹配....WA之...
其实显然是错的, 因为每次删去一堆边你不能保证这样的匹配是最优的.
然后发现有多重匹配这种东西....其实也就是给定{V}最多能匹配多少个u, 然后求最大匹配.
用 flag[v][k] 表示点v匹配到第k个点. >=0表示匹配的点是u = flag[v][k]. <0表示还未匹配到第k点.
对已经匹配满k个点的情况, dfs里要枚举v匹配的这k个u, 然后看有没有增广路, 有的话让出匹配.
这样, 二分答案就能求出最少是多少, 二分条件是 最大匹配是否等于人数(表示这样的限度是否足够容纳所有人).
貌似也可以用最大流+二分做.但是肯定是匹配好写点
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
inline int Rint() { int x; scanf("%d", &x); return x; }
inline int max(int x, int y) { return (x>y)? x: y; }
inline int min(int x, int y) { return (x<y)? x: y; }
#define FOR(i, a, b) for(int i=(a); i<=(b); i++)
#define FORD(i,a,b) for(int i=(a);i>=(b);i--)
#define REP(x) for(int i=0; i<(x); i++)
typedef long long int64;
#define INF (1<<30)
#define bug(s) cout<<#s<<"="<<s<<" "
// 1/ 多重匹配, v能匹配多个u.
// 2/ 指定每个v的匹配上限k, 若最大匹配=u. 则说明k足够大. 从而可以二分答案.
#define MAXN 1002
#define MAXM 502
#define MAXE MAXN*MAXM //最大边数
struct node
{
int u, v, w;
}a[MAXE];
int fa[MAXN], next[MAXE], idx;
int flag[MAXM][MAXN];
int tot[MAXM]; //tot[v] 表示v已经匹配的u数目
int vis[MAXM]; //dfs访问标记, 只用vis[v]. u是枚举一遍的.
int n, m;
void addedge(int u, int v, int w) { a[idx].u=u; a[idx].v=v; a[idx].w=w; next[idx]=fa[u]; fa[u]=idx++; }
int dfs(int u, int k) //匹配k
{
for(int e = fa[u]; e!=-1; e=next[e])
{
int v = a[e].v;
if(vis[v]) continue;
vis[v] = 1;
if(tot[v]<k) //tot[v]<k, 未满
{
flag[v][tot[v]++] = u;
return 1;
}
FOR(i, 0, k-1) //tot[v]=k, 满
if(dfs(flag[v][i], k))
{
flag[v][i] = u;
return 1;
}
}
return 0;
}
void print()
{
FOR(i, 1, m)
{
bug(i);bug(tot[i])<<endl;
}
}
int hungry(int k)
{
int ans = 0;
memset(flag, 0, sizeof(flag));
memset(tot, 0, sizeof(tot));
FOR(u, 1, n)
{
memset(vis, 0, sizeof(vis));
if(dfs(u, k))
ans++;
}
//bug(k)<<endl;
//print();
return ans;
}
int solve()
{
int l = 1, r = n; //每组几个人
while(l<r) //当 l==r跳出
{
int mid = (l+r)>>1;
if(hungry(mid) == n)
r = mid;
else
l = mid+1;
}
return l;
}
int main()
{
while(scanf("%d%d", &n, &m) == 2)
{
if(!n && !m) break;
idx = 0;
memset(fa, -1, sizeof(fa));
FOR(i, 1, n)
{
//bug(i);
char buf[20];
scanf("%s", buf);
int v;
char ch;
while(scanf("%d%c", &v, &ch) == 2)
{
//bug(v);bug(ch)<<endl;
v++; //1-th
addedge(i, v, 1);
if(ch == '\n') break;
}
}
printf("%d\n", solve());
}
}
有点像鸽巢的感觉...开始我想了一个, 以为是每次求最大匹配, 然后把已盖点的邻边全删掉, 然后看能求几次最大匹配....WA之...
其实显然是错的, 因为每次删去一堆边你不能保证这样的匹配是最优的.
然后发现有多重匹配这种东西....其实也就是给定{V}最多能匹配多少个u, 然后求最大匹配.
用 flag[v][k] 表示点v匹配到第k个点. >=0表示匹配的点是u = flag[v][k]. <0表示还未匹配到第k点.
对已经匹配满k个点的情况, dfs里要枚举v匹配的这k个u, 然后看有没有增广路, 有的话让出匹配.
这样, 二分答案就能求出最少是多少, 二分条件是 最大匹配是否等于人数(表示这样的限度是否足够容纳所有人).
貌似也可以用最大流+二分做.但是肯定是匹配好写点
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
inline int Rint() { int x; scanf("%d", &x); return x; }
inline int max(int x, int y) { return (x>y)? x: y; }
inline int min(int x, int y) { return (x<y)? x: y; }
#define FOR(i, a, b) for(int i=(a); i<=(b); i++)
#define FORD(i,a,b) for(int i=(a);i>=(b);i--)
#define REP(x) for(int i=0; i<(x); i++)
typedef long long int64;
#define INF (1<<30)
#define bug(s) cout<<#s<<"="<<s<<" "
// 1/ 多重匹配, v能匹配多个u.
// 2/ 指定每个v的匹配上限k, 若最大匹配=u. 则说明k足够大. 从而可以二分答案.
#define MAXN 1002
#define MAXM 502
#define MAXE MAXN*MAXM //最大边数
struct node
{
int u, v, w;
}a[MAXE];
int fa[MAXN], next[MAXE], idx;
int flag[MAXM][MAXN];
int tot[MAXM]; //tot[v] 表示v已经匹配的u数目
int vis[MAXM]; //dfs访问标记, 只用vis[v]. u是枚举一遍的.
int n, m;
void addedge(int u, int v, int w) { a[idx].u=u; a[idx].v=v; a[idx].w=w; next[idx]=fa[u]; fa[u]=idx++; }
int dfs(int u, int k) //匹配k
{
for(int e = fa[u]; e!=-1; e=next[e])
{
int v = a[e].v;
if(vis[v]) continue;
vis[v] = 1;
if(tot[v]<k) //tot[v]<k, 未满
{
flag[v][tot[v]++] = u;
return 1;
}
FOR(i, 0, k-1) //tot[v]=k, 满
if(dfs(flag[v][i], k))
{
flag[v][i] = u;
return 1;
}
}
return 0;
}
void print()
{
FOR(i, 1, m)
{
bug(i);bug(tot[i])<<endl;
}
}
int hungry(int k)
{
int ans = 0;
memset(flag, 0, sizeof(flag));
memset(tot, 0, sizeof(tot));
FOR(u, 1, n)
{
memset(vis, 0, sizeof(vis));
if(dfs(u, k))
ans++;
}
//bug(k)<<endl;
//print();
return ans;
}
int solve()
{
int l = 1, r = n; //每组几个人
while(l<r) //当 l==r跳出
{
int mid = (l+r)>>1;
if(hungry(mid) == n)
r = mid;
else
l = mid+1;
}
return l;
}
int main()
{
while(scanf("%d%d", &n, &m) == 2)
{
if(!n && !m) break;
idx = 0;
memset(fa, -1, sizeof(fa));
FOR(i, 1, n)
{
//bug(i);
char buf[20];
scanf("%s", buf);
int v;
char ch;
while(scanf("%d%c", &v, &ch) == 2)
{
//bug(v);bug(ch)<<endl;
v++; //1-th
addedge(i, v, 1);
if(ch == '\n') break;
}
}
printf("%d\n", solve());
}
}
相关文章推荐
- POJ 2289 Jamie's Contact Groups (二分+匹配/网络流)
- Poj 2289 Jamie's Contact Groups【二分+多重匹配】
- POJ 2289 Jamie's Contact Groups(多重匹配+二分)
- POJ 2289 Jamie’s Contact Groups-二分匹配&多重匹配
- poj2289--Jamie's Contact Groups(二分多重匹配)
- POJ 2289 Jamie's Contact Groups (二分答案+二分图的多重匹配)
- POJ 2289 Jamie's Contact Groups 多重匹配+二分
- POJ -2289 -Jamie's Contact Groups (二分图多重匹配)
- poj 2289 Jamie's Contact Groups 【二分 + 最大流】
- POJ2289-Jamie's Contact Groups(二分图多重匹配)
- poj2289 Jamie's Contact Groups(二分答案+最大流)
- poj 2289 Jamie's Contact Groups
- POJ2289 Jamie's Contact Groups(二分图多重匹配)
- Poj 2289 Jamie's Contact Groups 【最大流Dinic+二分】
- POJ 2289 Jamie's Contact Groups (二分答案+匹配流)
- Poj-2289 Jamie's Contact Groups 多重二分图匹配
- HDU1669 Jamie's Contact Groups (二分+二分图的多重匹配+一对多的匹配)
- POJ 2289——Jamie's Contact Groups——————【多重匹配、二分枚举匹配次数】
- POJ 2289 Jamie's Contact Groups (二分+dinic)
- 【二分图多重匹配 && Dinic】POJ - 2289 Jamie's Contact Groups