首先想,这里有一堆物品,你可以选择取用或者不取,每种有一定的收益,支付一定的代价,那么就决定是你了!背包!(抱歉不是dp,OJ会等你枚举个2n 次?)
权衡奖金与代价,这两者是有我没他的,怎么去处理呢?
建立最大权闭合子图:源点连正点权(即收益),容量为正;负点权(花费)连汇点,容量为负的负(花费较总收益来说原本就是负的,连得时候连正的就行),有关系的实验和器材之间连边容量为正无穷,答案为全部实验收益 - 上面图跑出来的最小割。
解释:正点之间与负点连边为INF最小割跑出来,要么割正边,意义就是舍弃这个方案,要么割负的负边,意义为买器材花钱,无论怎样,要是使得放弃亲戚钱与花的 买电视的钱最少(也就是最小割),答案即为最优。
而花的钱最少,都在割去的边内,我们可以求出最大流,剩下的权值就是我们的最小割。
由于本题与网络流24题中的 太空飞行问题 基本师出同源,这里套用别人题解中一张图来说明:
最大流求法还是dinic:
#include<bits/stdc++.h>
#define FOR(a, b, c) for(int a=b; a<=c; a++)
#define maxn 100005
#define maxm 55
#define hrdg 1000000007
#define zh 16711680
#define inf 2147483647
#define llinf 9223372036854775807
#define ll long long
#define pi acos(-1.0)
#define ls p<<1
#define rs p<<1|1
#define int long long
using namespace std;
inline int read(){
char c=getchar();long long x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n, m, u, v, d, depth[maxn];
int award, num, need, cost, sum = 0;
int ans;
int start, goal;
struct Edge{ int nex, to, dis;}edge[maxn*10];
int head[maxn*10], tot, cur[maxn*10];
void add_egde(int u, int v, int d){
edge[tot] = {head[u], v, d}; head[u] = tot++;
edge[tot] = {head[v], u, 0}; head[v] = tot++;
}
bool bfs(int start, int goal){
memset (depth, -1, sizeof(depth));
queue <int> q;
depth[start] = 0;
q.push(start);
while (!q.empty())
{
int now = q.front(); q.pop();
for (int i = head[now]; ~i; i = edge[i].nex)
{
int to = edge[i].to;
if (depth[to] < 0 && edge[i].dis > 0)
{
depth[to] = depth[now] + 1;
q.push(to);
}
}
}
return depth[goal] > 0;
}
int dfs(int now, int goal, int limit) {
if(now == goal) return limit;
for(int & i = cur[now]; ~i; i = edge[i].nex) {
int to = edge[i].to;
if(edge[i].dis > 0 && depth[to] > depth[now]) {
int d = dfs(to, goal, min(limit, edge[i].dis));
if(d > 0) {
edge[i].dis -= d;
edge[i ^ 1].dis += d;
return d;
}
}
}
depth[now] = -1;
return 0;
}
void init(){
memset(head, -1, sizeof(head));
tot = 0;
}
int dinic(int start, int goal){
int flow = 0, f;
while (bfs(start, goal))
{
for (int i = start; i <= goal; i++)
cur[i] = head[i];
while (f = dfs(start, goal, inf))
flow += f;
}
return flow;
}
signed main()
{
init();
n = read(); m = read();
start = 0; goal = n + m + 1;
for (int i = 1; i <= m; i++)
{
cost = read();
add_egde(i + n, goal, cost);
}
for (int i = 1; i <= n; i++)
{
award = read(); num = read();
add_egde(start, i, award);
sum += award; //总的正向收益累加
for (int j = 1; j <= num; j++)
{
need = read();
add_egde(i, need + n, inf);
}
}
ans = sum - dinic(start, goal); //总权值减去最大流
printf("%lld", ans);
return 0;
}