题意:Jamie有很多联系人,但是很不方便管理,他想把这些联系人分成组,已知这些联系人可以被分到哪个组中去,而且要求每个组的联系人上限最小,即有一整数k,使每个组的联系人数都不大于k,问这个k最小是多少?
思路:这个题很像以前的那个星际战舰的那个题,一般就是二分答案,对每一个值进行建图,求最大流,查看这个最大流是否等于总人数,虽然比较繁琐,但是思路还是很清晰的,之后就是SAP模板了......(每次建图的时候一定要对信息进行初始化啊!!!WA了N次!)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
#include <cmath>
#include <bitset>
#include <queue>
#include <vector>
using namespace std;
const int BORDER = (1<<20)-1;
const int MAXSIZE = 37;
const int MAXN = 1508;
const int INF = INT_MAX;
#define CLR(x,y) memset(x,y,sizeof(x))
#define ADD(x) x=((x+1)&BORDER)
#define IN(x) scanf("%d",&x)
#define OUT(x) printf("%d\n",x)
#define MIN(m,v) (m)<(v)?(m):(v)
#define MAX(m,v) (m)>(v)?(m):(v)
#define ABS(x) ((x)>0?(x):-(x))
int index,n,m,ans;
int adj[MAXN][MAXN],in[MAXN];
int nv,s,t;
int net[MAXN];
char str[20];
struct Edge{
int next,pair;
int v,cap,flow;
}edge[1500000];
int init()
{
index = 0;
CLR(net,-1);
CLR(adj,0);
CLR(in,0);
ans = 0;
return 0;
}
int input()
{
int i,j,tmp;
char ch;
getchar();
for(i = 1;i <= n; i++)
{
ch = getchar();
while((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
ch = getchar();
while(ch == ' ')
ch = getchar();
while(true)
{
j = 0;
while(ch >= '0' && ch <= '9')
{
j = j * 10 + ch - '0';
ch = getchar();
}
adj[i][j + 1] = 1;
in[j + 1]++;
if(ch == '\n') break;
while(ch == ' ') ch = getchar();
}
}
s = 0;
nv = n + m + 2;
t = nv - 1;
return 1;
}
void add_edge(const int& u,const int& v,const int& val)
{
edge[index].next = net[u];
net[u] = index;
edge[index].v = v;
edge[index].cap = val;
edge[index].flow = 0;
edge[index].pair = index+1;
++index;
edge[index].next = net[v];
net[v] = index;
edge[index].v = u;
edge[index].cap = 0;
edge[index].flow = 0;
edge[index].pair = index - 1;
++index;
}
int make_graph(const int& limit)
{
int i,j,tmp,k,u,v,val;
CLR(net,-1);
index = 0;
for(i = 1; i <= n; ++i)
add_edge(s,i,1);
for(i = 1; i <= n; ++i)
{
for(j = 1; j <= m; ++j)
if(adj[i][j])
add_edge(i,j+n,1);
}
for(i = 1; i <= m; ++i)
if(in[i] >= limit)
add_edge(i+n,t,limit);
else
add_edge(i+n,t,in[i]);
return 0;
}
int ISAP()
{
long numb[MAXN],dist[MAXN],curedge[MAXN],pre[MAXN];
long cur_flow,max_flow,u,tmp,neck,i;
memset(dist,0,sizeof(dist));
memset(numb,0,sizeof(numb));
for(i = 1 ; i <= nv ; ++i)
curedge[i] = net[i];
numb[nv] = nv;
max_flow = 0;
u = s;
while(dist[s] < nv)
{
/* first , check if has augmemt flow */
if(u == t)
{
cur_flow = INF;
for(i = s; i != t;i = edge[curedge[i]].v)
{
if(cur_flow > edge[curedge[i]].cap)
{
neck = i;
cur_flow = edge[curedge[i]].cap;
}
}
for(i = s; i != t; i = edge[curedge[i]].v)
{
tmp = curedge[i];
edge[tmp].cap -= cur_flow;
edge[tmp].flow += cur_flow;
tmp = edge[tmp].pair;
edge[tmp].cap += cur_flow;
edge[tmp].flow -= cur_flow;
}
max_flow += cur_flow;
u = s;
}
/* if .... else ... */
for(i = curedge[u]; i != -1; i = edge[i].next)
if(edge[i].cap > 0 && dist[u] == dist[edge[i].v]+1)
break;
if(i != -1)
{
curedge[u] = i;
pre[edge[i].v] = u;
u = edge[i].v;
}else{
if(0 == --numb[dist[u]]) break;
curedge[u] = net[u];
for(tmp = nv,i = net[u]; i != -1; i = edge[i].next)
if(edge[i].cap > 0)
tmp = tmp<dist[edge[i].v]?tmp:dist[edge[i].v];
dist[u] = tmp + 1;
++numb[dist[u]];
if(u != s) u = pre[u];
}
}
return max_flow;
}
int work()
{
int low,high,mid,tmp;
int pre;
low = 0;
high = -1;
for(int i = 1; i <= m; ++i)
high = MAX(high,in[i]);
while(low <= high)
{
mid = (low + high)>>1;
make_graph(mid);
tmp = ISAP();
if(tmp == n)
{
pre = mid;
high = mid - 1;
}
else
low = mid + 1;
}
printf("%d\n",pre);
return 0;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(!n && !m)
break;
init();
input();
work();
}
return 0;
}