动物森友会(最大流,二分,科大讯飞杯)

题意

有个人有\(n\)个任务要做。

每个任务需要做\(c_i\)次,一周当中有\(m_i\)天可以做,分别是\(a_{i_1}, a_{i_2}, \dots, a_{i_{m_i}}\)

这个人每天最多可以做\(k\)次任务。

问最少需要多少天可以将这些任务做完。

数据范围

\(1 \leq n \leq 1000\)
\(1 \leq k \leq 100\)
\(1 \leq c_i \leq 100000\)
\(1 \leq m_i \leq 7\)
\(1 \leq a_{i_j} \leq 7\)

思路

考虑网络流,其实有点类似于二分图多重匹配问题。

二分天数\(mid\),判断使用\(mid\)天能否做完所有任务,所有任务数为\(\sum_{i = 1}^n c_i\)

设虚拟源点\(S\),虚拟汇点\(T\)

源点向一周中的每一天连边,容量是当天可做的任务数,即:\(k * (mid / 7) + k * (i \leq mid \% 7)\)

一周中的每一天向可做的任务连边,容量是\(inf\)

每个任务向汇点连边,容量是这项任务需要做的次数

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

typedef long long ll;

const int N = 1010, M = 20 * N;
const ll inf = 1e10;

int n, k, S, T;
int h[N], e[M], ne[M], idx;
ll f[M];
int d[N], cur[N];
int c[N], m[N];
vector<int> vec[N];
ll sum;

void add(int a, int b, ll c)
{
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++;
}

bool bfs()
{
    memset(d, -1, sizeof(d));
    queue<int> que;
    que.push(S);
    d[S] = 0, cur[S] = h[S];
    while(que.size()) {
        int t = que.front();
        que.pop();
        for(int i = h[t]; ~i; i = ne[i]) {
            int ver = e[i];
            if(d[ver] == -1 && f[i]) {
                d[ver] = d[t] + 1;
                cur[ver] = h[ver];
                if(ver == T) return true;
                que.push(ver);
            }
        }
    }
    return false;
}

ll find(int u, ll limit)
{
    if(u == T) return limit;
    ll flow = 0;
    for(int i = cur[u]; ~i && flow < limit; i = ne[i]) {
        cur[u] = i;
        int ver = e[i];
        if(d[ver] == d[u] + 1 && f[i]) {
            ll t = find(ver, min(f[i], limit - flow));
            if(!t) d[ver] = -1;
            f[i] -= t, f[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}

ll dinic()
{
    ll res = 0, flow;
    while(bfs()) {
        while(flow = find(S, inf)) {
            res += flow;
        }
    }
    return res;
}

bool check(int mid)
{
    memset(h, -1, sizeof(h));
    idx = 0;
    for(int i = 1; i <= 7; i ++) {
        ll x = (ll)k * (mid / 7) + (ll)(i <= mid % 7) * k;
        add(S, i, x);
    }
    for(int i = 1; i <= n; i ++) {
        for(auto j : vec[i]) {
            add(j, i + 7, inf);
        }
    }
    for(int i = 1; i <= n; i ++) {
        add(i + 7, T, c[i]);
    }
    return sum <= dinic();
}

int main()
{
    scanf("%d%d", &n, &k);
    S = 0, T = n + 7 + 1;
    for(int i = 1; i <= n; i ++) {
        int a, b;
        scanf("%d%d", &a, &b);
        sum += a;
        c[i] = a, m[i] = b;
        for(int j = 1; j <= m[i]; j ++) {
            int x;
            scanf("%d", &x);
            vec[i].push_back(x);
        }
    }
    int l = 0, r = 1e9;
    while(l < r) {
        int mid  = l + r >> 1;
        if(check(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%d\n", r);
    return 0;
}
posted @ 2021-02-05 10:57  pbc的成长之路  阅读(54)  评论(0编辑  收藏  举报