传送门:HDU-6126
有n个小朋友,标号为1到n,你要给每个小朋友至少1个且至多m个的糖果。小朋友们共提出k个要求,每个要求包括三个整数x,y,z,表示x号小朋友得到的糖果数减去y号小朋友得到的糖果数,结果应当不大于z。如果你给i号小朋友j颗糖果,他会获得wi,j的满意度,你需要最大化所有小朋友的满意度之和。1≤n,m≤50,1≤k≤150,1≤wi,j≤103。
题解:网络流最小割
首先考虑没有限制的情况:对于每个人,当然要选取愉悦值最高的情况,如果要用最大流表示,用(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|1
using 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;
}