HDU-6126 Give out candies(最小割)

来源:互联网 发布:滨州行知中学电话 编辑:程序博客网 时间:2024/06/08 01:04

传送门:HDU-6126

nn个小朋友,标号为11nn,你要给每个小朋友至少11个且至多mm个的糖果。小朋友们共提出kk个要求,每个要求包括三个整数x,y,zx,y,z,表示xx号小朋友得到的糖果数减去yy号小朋友得到的糖果数,结果应当不大于zz。如果你给ii号小朋友jj颗糖果,他会获得w_{i,j}wi,j的满意度,你需要最大化所有小朋友的满意度之和。1\leq n,m\leq50,1\leq k\leq150,1\leq w_{i,j}\leq10^31n,m50,1k150,1wi,j103

题解:网络流最小割

首先考虑没有限制的情况:对于每个人,当然要选取愉悦值最高的情况,如果要用最大流表示,用(i,j)表示第i个人获得j个糖果,愉悦值为w(i,j),边的容量就可以设为F-w(i,j),F为一个足够大的正整数,将(i,j)->(i,j+1)用这条边连接起来,如果这条边是最小割,就表示送给第i个人j个糖果的愉悦值是最高的。再让源点与(i,1)连一条容量为正无穷的边,(i,m)与汇点连容量为F-w(i,m)的边

再考虑有限制的情况:发给第x个人的糖果数-发给第y个人的糖果数不能大于z(这里只考虑z>0的情况)
设第i个人的糖果数为a[i],则a[x]-a[y]<=z --> a[x]-z<=a[y]
因此可以转化为:如果x拿了a[x]个糖果,那么y的糖果就不能少于a[x]-z
要如何表示这种冲突呢?首先肯定要想到建无限容量的边,我们将(x,j)->(y,j-z)连一条容量为正无穷的边,
根据容量无穷大边的性质,在这条边的两端至少有一个割点(不存在x的割在某个大于j的点,y的割在某个小于j-z的点)

当限制条件发生冲突时,比如a[x]<=a[y],a[y]+1<=a[x],这样源点与汇点之间会有一条由正无穷的边构成的路径,因此只要流量为正无穷时,就表示发生了冲突,输出-1

#include<bits/stdc++.h>#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1using namespace std;typedef long long LL;typedef pair<int, int> PII;const LL mod = 1e9 + 7;int n, m, k;const int inf = 0x3f3f3f3f;const int MX = 2555;const int MXE = 4 * MX * MX;struct MaxFlow {    struct Edge {        int v, w, nxt;    } edge[MXE];    int tot, num, s, t;    int head[MX];    void init() {        memset(head, -1, sizeof(head));        tot = 0;    }    void add(int u, int v, int w) {        edge[tot].v = v;        edge[tot].w = w;        edge[tot].nxt = head[u];        head[u] = tot++;        edge[tot].v = u;        edge[tot].w = 0;        edge[tot].nxt = head[v];        head[v] = tot++;    }    int  d[MX], vis[MX], gap[MX];    void bfs() {        memset(d, 0, sizeof(d));        memset(gap, 0, sizeof(gap));        memset(vis, 0, sizeof(vis));        queue<int>q;        q.push(t);        vis[t] = 1;        while (!q.empty()) {            int u = q.front();            q.pop();            for (int i = head[u]; ~i; i = edge[i].nxt) {                int v = edge[i].v;                if (!vis[v]) {                    d[v] = d[u] + 1;                    gap[d[v]]++;                    q.push(v);                    vis[v] = 1;                }            }        }    }    int last[MX];    int dfs(int u, int f) {        if (u == t) return f;        int sap = 0;        for (int i = last[u]; ~i; i = edge[i].nxt) {            int v = edge[i].v;            if (edge[i].w > 0 && d[u] == d[v] + 1) {                last[u] = i;                int tmp = dfs(v, min(f - sap, edge[i].w));                edge[i].w -= tmp;                edge[i ^ 1].w += tmp;                sap += tmp;                if (sap == f) return sap;            }        }        if (d[s] >= num) return sap;        if (!(--gap[d[u]])) d[s] = num;        ++gap[++d[u]];        last[u] = head[u];        return sap;    }    int solve(int st, int ed, int n) {        int flow = 0;        num = n;        s = st;        t = ed;        bfs();        memcpy(last, head, sizeof(head));        while (d[s] < num) flow += dfs(s, inf);        return flow;    }} F;int code(int i, int j) {    if (j == m + 1) return n * m + 1;    return (i - 1) * m + j;}int main() {    int T;    //freopen("in.txt", "r", stdin);    scanf("%d", &T);    while (T--) {        scanf("%d%d%d", &n, &m, &k);        F.init();        for (int i = 1; i <= n; i++) {            F.add(0, code(i, 1), inf);            for (int j = 1, w; j <= m; j++) {                scanf("%d", &w);                F.add(code(i, j), code(i, j + 1), 1000 - w);            }        }        for (int i = 1, x, y, z; i <= k; i++) {            scanf("%d%d%d", &x, &y, &z);            for (int j = 1; j <= m; j++) {                if (j - z > 0) F.add(code(x, j), code(y, min(j - z, m + 1)), inf);            }        }        int cnt = F.solve(0, n * m + 1, n * m + 2);        if (cnt >= inf) printf("-1\n");        else printf("%d\n", n * 1000 - cnt);    }    return 0;}