牛客/科大讯飞杯 L题动物森友会 (二分最大流)

https://ac.nowcoder.com/acm/problem/205306

贪心和dp做不了,所以想到这是一个匹配问题。

建立源点s和汇点t,源点s连接一周的7天,容量为cnt*总天数/7,特判总天数%7有余数的情况。

一周的7天作为7个节点连接n个task,容量设定为inf。

n个task连接汇点,容量为m[i]

由于花费的天数越多,跑出的最大流越大,最大流具有单调性,故可以二分总天数然后重新建图,去跑最大流,判断满流是否等于题目所需,最终找到最少的天数即可。

  1 #include<bits/stdc++.h>
  2 typedef long long ll;
  3 using namespace std;
  4 const int maxn = 1200;
  5 const int inf = 1e9+10;
  6 int tot,s,t,n,cnt,sum;//tot指向空结点,源点s,汇点t
  7 int head[maxn],d[maxn],cur[maxn];//head指向边号
  8 int c[maxn],m[maxn];
  9 int g[maxn][10];
 10 struct edge
 11 {
 12     int next,to;
 13     int cap;
 14 }e[maxn*maxn];
 15 void add(int u,int to,ll w){
 16     e[tot].next = head[u];//边号是tot,head[u]是u节点指向的上一条边
 17     e[tot].to = to;//tot边指向to
 18     e[tot].cap = w;//容量为w
 19     head[u] = tot++;//head[u]变更为tot
 20 }
 21 bool bfs(){//构建层次网络
 22     for(int i = 0;i<=n+10;i++) d[i] = 0,cur[i] = head[i];//初始化深度数组
 23     queue<int> q;
 24     q.push(s);
 25     d[s] = 1;
 26     while(!q.empty()){
 27         int v = q.front();//当前节点v
 28         q.pop();
 29         for (int i = head[v];~i;i = e[i].next)
 30         {
 31             if(!d[e[i].to] && e[i].cap){//如果to节点没访问过,且仍有容量
 32                 d[e[i].to] = d[v] + 1;//深度+1
 33                 q.push(e[i].to);//入队列
 34             }
 35         }
 36     }
 37     return d[t]!=0;//true返回找到增广路
 38 }
 39 int dfs(int end,int u,int Max){//dfs查找所有的增广路并做流量调整
 40     if(u == end || !Max) return Max;//返回找到的流量
 41     int res = 0;
 42     for (int i = cur[u];~i;i = e[i].next)//当前弧优化,从cur[u]这条边开始
 43     {   //遍历cur所连的边,边是e[i]
 44         cur[u] = i;
 45         if(d[e[i].to] == d[u] + 1 && e[i].cap>0){//如果u和to在其中一条增广路上
 46             int t = dfs(end,e[i].to,min(e[i].cap,Max));//递归下去
 47             e[i].cap-= t;//容量减去t
 48             e[i^1].cap+=t;//增加反边容量
 49             Max-=t;//可流入流量减t
 50             res+=t;//最大流+t
 51             if(Max == 0) break;//如果残余流量为0,终止
 52         }
 53     }
 54     return res;//返回
 55 }
 56 int dinic(){
 57     int res = 0;
 58     while(bfs()){//存在增广路 就dfs一遍
 59         res +=dfs(t,s,inf);//寻找s到t的流量
 60     }
 61     return res;
 62 }
 63 bool check(int day){
 64     tot = 0;
 65     memset(head,-1,sizeof(head));
 66     for (int i = 1; i <= 7; ++i)
 67     {
 68         /* code */
 69         if(day%7 >=i){
 70             add(s,i,(day/7)*cnt+cnt);//天数建边
 71             add(i,s,0);
 72         }
 73         else{
 74             add(s,i,(day/7)*cnt);
 75             add(i,s,0);
 76         }
 77     }
 78     for (int i = 1; i <= n; ++i)
 79     {
 80         /* code */
 81         add(i+7,t,c[i]);//task连接汇点t的边,容量为c[i];
 82         add(t,i+7,0);
 83         for(int j = 1;j<=7;j++){
 84             if(g[i][j]) add(j,i+7,inf),add(i+7,j,0);
 85         }
 86   
 87     }
 88     int MaxFlow = dinic();
 89     return MaxFlow >= sum;
 90 }
 91 int main(){
 92     scanf("%d%d",&n,&cnt);
 93     s = 0, t = 7+n+1;
 94     for(int i = 1;i<=n;i++){
 95         scanf("%d%d",&c[i],&m[i]);
 96         sum+=c[i];
 97         int t ;
 98         for(int j = 1;j<=m[i];j++) {scanf("%d",&t);g[i][t] = 1;}
 99     }
100     int l = 1, r = (sum/cnt+1)*7+100;
101     int ans = 0;
102     while(l<=r){
103         int mid = l+r>>1;
104         if(check(mid)){//二分总天数
105             ans = mid;
106             r = mid - 1;
107         }
108         else{
109             l = mid + 1;
110         }
111     }
112     printf("%d
", ans);
113     return 0;
114 }
原文地址:https://www.cnblogs.com/AaronChang/p/12733736.html