m个保安监控 n 个钻石。钻石价值
a
i
a_i
ai,保安小费
b
i
b_i
bi。小偷若向保安支付小费,则保安放松监控,小偷将偷得钻石。
问:如何支付小费将获得最大收益。
输入:
1
3 3 // n,m
3 4 5 // 钻石的价值
1 2 3 // 小费
101 // 监控第1颗钻石的保安
001
101
分析
一般场景
放弃若干钻石,支付若干保安小费,此时的收益:
∑
i
=
1
n
a
i
−
(
∑
j
=
1
α
a
k
j
+
∑
j
=
1
β
b
k
j
)
,
1
≤
α
≤
n
,
1
≤
β
≤
m
\sum_{i=1}^n a_i - ( \sum_{j=1}^\alpha a_{k_j} + \sum_{j=1}^\beta b_{k_j}),1 \leq\alpha\leq n,1 \leq\beta \leq m
i=1∑nai−(j=1∑αakj+j=1∑βbkj),1≤α≤n,1≤β≤m
当放弃的钻石价值及支付的小费总和最小时,收益最大
∑
j
=
1
α
a
k
j
+
∑
j
=
1
β
b
k
j
\sum_{j=1}^\alpha a_{k_j} + \sum_{j=1}^\beta b_{k_j}
j=1∑αakj+j=1∑βbkj
模型转换
最大值
→
\rightarrow
→ 最小值,转化为最小割
剩余的钻石与未支付小费的保安无关
构图
放弃钻石,等效于删除边
支付小费,等效于删除边
源点与钻石连边,权值为钻石的价值
汇点与保安连边,权值为小费
监控关系连边,权值无穷大
代码
#include<bits/stdc++.h>usingnamespace std;constint inf =0x3f3f3f3f;constint MXN =205;int n, m, g[MXN][MXN], pre[MXN], flow[MXN];boolbfs(int s,int t,int v){// 搜索增广路memset(pre,-1,sizeof pre),memset(flow, inf,sizeof flow);
queue<int> q;
q.push(s), pre[s]=0, flow[s]= inf;// pre:路径兼标记while(!q.empty()){int last = q.front();if(last == t)break;for(int i =1; i <= v;++i)if(pre[i]==-1&& g[last][i]>0)
pre[i]= last, flow[i]=min(flow[last], g[last][i]), q.push(i);
q.pop();}return pre[t]!=-1;}voidupdate(int s,int t){// 更新残留网络for(int fa, now = t; now != s; now = fa)
fa = pre[now], g[fa][now]-= flow[t], g[now][fa]+= flow[t];}intmain(){int t, x, sum, maxflow;char str[MXN];scanf("%d",&t);while(t--){memset(g,0,sizeof g), sum =0, maxflow =0;scanf("%d%d",&n,&m);for(int i =1; i <= n;++i)scanf("%d",&x), sum += x, g[n+m+1][i]= x;for(int i =1; i <= m;++i)scanf("%d",&x), g[n+i][n+m+2]= x;for(int i =1; i <= n;++i){scanf("%s", str+1);for(int j =1; j <= m;++j)if(str[j]=='1') g[i][n+j]= inf;}while(bfs(n+m+1, n+m+2, n+m+2)) maxflow += flow[n+m+2],update(n+m+1, n+m+2);printf("%d\n", sum - maxflow);}return0;}