1669 DINIC+二分

题意:
      给你一些名单,和每个名单可以放在哪个分组里,现在要求你把所有的人都分到一个他属于的分组(之一),然后问你分组中最多的那个人数最少是多少...


思路:
     二分最多的最少,然后用最大流去判断分组是否合理,建图如下
         
    s 到 所有人连一条边 流量是1
    所有分组 到 t 连一条边,流量是mid
    然后把所有人和自己属于的分组连边,流量1
    
    跑一边最大流,如果答案等于 N(人数) 那么当前二分满足

    up = mid - 1 ........


#include<stdio.h>
#include<string.h>
#include<queue>

#define N_node 1500 + 50
#define N_edge 2000000 + 10000
#define INF 1000000000

using namespace std;

typedef struct
{
   int to ,next ,cost;
}STAR;

typedef struct
{
   int x ,t;
}DEP;

typedef struct
{
   int a ,b;
}EDGE;

STAR E[N_edge];
DEP xin ,tou;
EDGE edge[N_edge];
int list[N_node] ,tot;
int list1[N_node];
int deep[N_node];
char str[1000000];

void add(int a ,int b ,int c)
{
   E[++tot].to = b;
   E[tot].cost = c;
   E[tot].next = list[a];
   list[a] = tot;
   
   E[++tot].to = a;
   E[tot].cost = 0;
   E[tot].next = list[b];
   list[b] = tot;
}

int minn(int x ,int y)
{
   return x < y ? x : y;
}

bool BFS_deep(int s ,int t ,int n)
{
   memset(deep ,255 ,sizeof(deep));
   xin.x = s;
   xin.t = 0;
   deep[s] = 0;
   queue<DEP>q;
   q.push(xin);
   while(!q.empty())
   {
      tou = q.front();
      q.pop();
      for(int k = list[tou.x] ;k ;k = E[k].next)
      {
         xin.x = E[k].to;
         xin.t = tou.t + 1;
         if(!E[k].cost || deep[xin.x] != -1)
         continue;
         deep[xin.x] = xin.t;
         q.push(xin);
      }
   }
   for(int i = 0 ;i <= n ;i ++)
   list1[i] = list[i];
   return deep[t] != -1;
}


int DFS_flow(int s ,int t ,int flow)
{
   if(s == t) return flow;
   int nowflow = 0;
   for(int k = list1[s] ;k ;k = E[k].next)
   {
      list1[s] = k;
      int to = E[k].to;
      int c  = E[k].cost;
      if(deep[to] != deep[s] + 1 || ! E[k].cost)
      continue;
      int tmp = DFS_flow(to ,t ,minn(c ,flow - nowflow));
      nowflow += tmp;
      E[k].cost -= tmp;
      E[k^1].cost += tmp;
      if(nowflow == flow) break;
   }
   if(!nowflow) deep[s] = 0;
   return nowflow;
}

int DINIC(int s ,int t ,int n)
{
   int ans = 0;
   while(BFS_deep(s ,t ,n))
   {
      ans += DFS_flow(s ,t ,INF);
   }
   return ans;
}

bool ok(int mid ,int tt ,int n ,int m)
{
   memset(list ,0 ,sizeof(list));
   tot = 1;
   int i;
   for(i = 1 ;i <= n ;i ++)
   add(0 ,i ,1);
   for(i = 1 ;i <= m ;i ++)
   add(n + i ,n + m + 1 ,mid);
   for(i = 1 ;i <= tt ;i ++)
   add(edge[i].a ,edge[i].b + n ,1);
   return DINIC(0 ,n + m + 1 ,n + m + 1) == n;
}
   
   
   
   
   

int main ()
{
   int n ,m ,i ,j;
   while(~scanf("%d %d" ,&n ,&m) && n + m)
   {
      getchar();
      int tt = 0;
      for(i = 1 ;i <= n ;i ++)
      {
         gets(str);
         int l = strlen(str) - 1;
         j = 0;
         while(str[j] != ' ')
         j++;
         int num = 0;
         for(j++ ;j <= l ;j ++)
         {
            if(str[j] == ' ')
            {
               edge[++tt].a = i;
               edge[tt].b = num + 1;
               num = 0;
               continue;
            }
            num = num * 10 + str[j] - 48;
         }
         edge[++tt].a = i;
         edge[tt].b = num + 1;
      }
      
      int low ,up ,mid;
      low = 0 ,up = n;
      int ans;
      while(low <= up)
      {
         mid = (low + up) >> 1;
         if(ok(mid ,tt ,n ,m))
         {
            ans = mid;
            up = mid - 1;
         }
         else
         low = mid + 1;
      }
      printf("%d\n" ,ans);
   }
   return 0;
}
       



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值