• 牛客上大5278 L. 动物森友会| 二分 最大流


    题目地址:https://ac.nowcoder.com/acm/contest/5278/L

    思路

    满足单调性质: 天数越多,跑出的最大流越大(即能完成的任务数量越多)
    所以二分天数,在当前天数下建图,跑最大流;

    建模如下:

    一、四种点:

    源点s、汇点t、周日期1~7、任务事件

    源点编号:s = 0,
    周编号:s+1,s+2....s+7,
    任务编号:s+7+1,s+7+2...s+7+n,
    汇点编号:t = s+7+n+1

    二、一共有三种边

    1.源点s->周1~周7 容量为mid/7*ee + (mid%7 >=i) * ee。(这一周日期下能做的任务总次数)
    2.任务1~n->汇点t 容量为c[i]。(需要的完成的次数)
    3.周->任务 容量为无穷大。(一天可以做无限次同一个任务)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int inf = (1u<<31)-1, maxn = 1500, maxm = 100000;
    struct edge { int to, nex, cap; } e[maxm];
    int tot, head[maxn], dep[maxn],cur[maxn]; // 初始化tot=2, head[0..N]=-1
    
    int c[maxn], m[maxn], g[maxn][10];
    int sum = 0;
    int n,ee;
    int s = 0, t = 0;
    
    //板子开始
    void addedge(int u, int v, int cap, int rev = 0) {
        e[tot] = edge { v, head[u], cap }; head[u] = tot++;
        e[tot] = edge { u, head[v], rev }; head[v] = tot++;
    }
    
    bool bfs(int s, int t) {
    	for(int i=0;i<=n+10;i++) cur[i]=head[i];
        static int Q[maxn]; int front = 0, rear = 0;
        memset(dep, 0, sizeof(dep)); Q[rear++] = s, dep[s] = 1;
        while (front != rear) {
            int u = Q[front++], v;
            for (int i = head[u]; ~i; i = e[i].nex) {
                if (e[i].cap > 0 && dep[v = e[i].to] == 0) {
                    dep[v] = dep[u] + 1;
                    if (v == t) return true;
                    Q[rear++] = v;
                }
            }
        }
        return false;
    }
    
    int dfs(int u, int t, int f) {
        if (u == t) return f; int d, v, c = 0;
        for (int i = cur[u]; i!=-1; i = e[i].nex) {
        	cur[u] = i; //当前弧优化 
            if (e[i].cap > 0 && dep[u] + 1 == dep[v = e[i].to]) {
                d = dfs(v, t, min(f - c, e[i].cap));
                if (d > 0) {
                    e[i].cap -= d, e[i^1].cap += d, c += d;
                    if (f == c) break;
                } else dep[v] = -1;
            }
        }
        return c;
    }
    
    int dinic(int s, int t) {
        int maxflow = 0;
        while (bfs(s, t)){
            maxflow += dfs(s, t, inf);
    	}
        return maxflow;
    }
    //板子结束
    
    
    //正文开始
    
    
    bool check(int mid){
    	tot = 2; //已有s编号(0),t编号(1) 
    	memset(head,-1,sizeof(head));
    	for(int i=1;i<=7;i++){ //1.源点->周  容量 mid/7*ee + (mid%7 >=i) * ee
    		if(mid%7 >= i){ //最后一个不完整的一周 判是否过了周i
    			addedge(s,i,(mid/7)*ee + ee);  //根据当前天数建边的容量
    		}else{
    			addedge(s,i,(mid/7)*ee);
    		}
    	}
    	for(int i=1;i<=n;i++){
    		addedge(i+7,t,c[i]); //2.任务编号i+7->汇点编号t  容量c[i]
    		for(int j=1;j<=7;j++){
    			if(g[i][j]){
    				addedge(j,7+i,inf); //3.周编号j->任务编号7+i inf
    			} 
    		} 
    	}
    	int maxflow = dinic(s,t); //跑出最大流 
    	return maxflow >= sum;
    }
    
    int main(){
        scanf("%d%d",&n,&ee);
        s = 0, t = 7+n+1; //原点为0 汇点为7+n+1 
    	sum = 0;
        for (int i=1; i<=n; i++){
            scanf("%d%d", &c[i], &m[i]);
            sum += c[i];
            for (int j = 1, d; j<=m[i]; j++) scanf("%d", &d), g[i][d] = 1;
        }
        int l=1, r=(sum/ee+1)*7+100;
        int ans = 0;
        //满足单调性质: 天数越多,跑出的最大流越大(即能完成的任务数量越多)
        while (l <= r){ //二分出 最少天数 
            int mid = l+r>>1;
            if(check(mid)){ //跑最大流看是否 当前天数下的最大流量>=总任务数 
                ans = mid;
                r = mid - 1;
    		}else{
    		    l = mid + 1;
    		}
        }
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    Unity Shader 之 渲染流水线
    C# 如何快速取到enum中的枚举数量
    Unity NGUI ScrollView 苹果式滑动
    多元线性回归~ML
    梯度下降~ML
    代价函数~ML
    ML~线性代数~python
    unity 大游戏使用什么框架
    C# Activator.CreateInstance()方法使用
    VSync Count 垂直同步
  • 原文地址:https://www.cnblogs.com/fisherss/p/12730782.html
Copyright © 2020-2023  润新知