Time Limit: 7000MS | Memory Limit: 65536K | |
Total Submissions: 6902 | Accepted: 2261 |
Description
Input
Output
Sample Input
3 2 John 0 1 Rose 1 Mary 1 5 4 ACM 1 2 3 ICPC 0 1 Asian 0 2 3 Regional 1 2 ShangHai 0 2 0 0
Sample Output
2 2
题意:有 n 个人 , m 个组(0 ~ m - 1)。每个人只能选择一个组,现在给出每个人可以选择的组号,设拥有人数最多的小组有 sum个人,问sum的最小值。
解析:要求最大值的最小化,第一反应就是用二分查找。二分枚举人最多的小组拥有的人数mid。
首先构造网络流, 建立超级源点, 超级汇点。
源点到每个人建边,权值为1。
每个人到可选择的小组建边,权值为1。
每个小组到汇点建边,权值为mid。
每次跑一遍网络流,如果满流的话,人数最多的小组拥有 mid 人,这种情况是可能存在的,如果当前大值的最小值 < mid, 更新最大值的最小值。
#include
#include
#include
#include
#define maxn 2000
#define maxm 1000000
#define INF 0x3f3f3f3f
using namespace std;
int n, m;
struct node {
int u, v, cap, flow, next;
};
node edge[maxm];
node newedge[maxm];
int head[maxn], cnt;
int newhead[maxn], newcnt;
int cur[maxn];
int dist[maxn], vis[maxn];
void init(){
cnt = 0;
memset(head, -1, sizeof(head));
}
void add(int u, int v, int w){
node E;
edge[cnt] = {u, v, w, 0, head[u]};
head[u] = cnt++;
edge[cnt] = {v, u, 0, 0 ,head[v]};
head[v] = cnt++;
}
void getmap(){
char str[100];
for(int i = 1; i <= n; ++i){
add(0, i, 1);//源点到每个人建边,权值为1
scanf("%s", str);
int a;
while(getchar() != '\n'){
scanf("%d", &a);
add(i, a + 1 + n, 1);//每个人到对应的组建边,权值为1
}
}
}
bool BFS(int st ,int ed){
queueq;
memset(vis, 0 ,sizeof(vis));
memset(dist, -1, sizeof(dist));
vis[st] = 1;
dist[st] = 0;
q.push(st);
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = head[u]; i != -1; i = edge[i].next){
node E = edge[i];
if(!vis[E.v] && E.cap > E.flow){
vis[E.v] = 1;
dist[E.v] = dist[u] + 1;
if(E.v == ed)
return true;
q.push(E.v);
}
}
}
return false;
}
int DFS(int x, int ed, int a){
if(x == ed || a == 0)
return a;
int flow = 0, f;
for(int &i = cur[x]; i != -1; i = edge[i].next){
node &E = edge[i];
if(dist[E.v] == dist[x] + 1 && (f = DFS(E.v, ed, min(a, E.cap - E.flow))) > 0){
E.flow += f;
edge[i ^ 1].flow -= f;
a -= f;
flow += f;
if(a == 0)
break;
}
}
return flow;
}
int maxflow(int st, int ed){
int flowsum = 0;
while(BFS(st,ed)){
memcpy(cur, head, sizeof(head));
flowsum += DFS(st, ed, INF);
}
return flowsum;
}
int main(){
while(scanf("%d%d", &n, &m), n || m){
init();
getmap();
memcpy(newhead, head, sizeof(head));
memcpy(newedge, edge, sizeof(edge));
newcnt = cnt;
int l = 0, r = n, mid;
int ans = n;
while(r >= l){
memcpy(head, newhead, sizeof(newhead));
memcpy(edge, newedge, sizeof(newedge));
cnt = newcnt;
mid = (l + r) / 2;
for(int i = 1 ;i <= m; ++i){
add(i + n, n + m + 1, mid);//每个组向汇点建边
}
if(maxflow(0, n + m + 1) == n){
ans = min(ans, mid);
r = mid - 1;
}
else
l = mid + 1;
}
printf("%d\n", ans);
}
return 0;
}