牛客/科大讯飞杯 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\n", ans); 113 return 0; 114 }