题面

分析

这类题叫最大权闭合图问题。
有向图的闭合图是指每个点的后驱节点都在图中。
这道题中,一些实验和它对应的仪器形成了个闭合二分图,一边点权是正,一边是负,总收益是权值和。
怎么最大化权值和呢?
建超级源点 s s s 和超级汇点 t t t
s s s 到每个实验连边,边权为 a i a_i ai
从 每个仪器到 t t t 连边,边权为 b i b_i bi 的绝对值
每个实验向对应仪器连边,边权为 i n f inf inf
那么 最大权值和 = 正权和 - 最小割
为什么捏?
选择了哪些实验方案和它对应的仪器形成了一个闭合图,这个闭合图的权值是 s s s 相连的正权和减去不与 t t t 相连的负权绝对值的和
而一个割的权值是 不与 s s s 相连的正权和加上不与 t t t 相连的负权绝对值的和
于是 闭合图权值 + 对应的割的权值 = 正权和
那么 最大权闭合图就是最小割割完后的与 s s s 相连的点集

代码如下

#include <bits/stdc++.h>
#define N 500005
#define inf 2147483647
#define LL long long
using namespace std;
struct node{
	int a, b, c, n;
}d[N * 2];
int dep[N], h[N], cur[N], a[105][105], n, m, f[N], vis[55], cnt = 1, tot, s, t, sum;
LL ans, ans1;
void cr(int a, int b, int c){
	d[++cnt].a = a; d[cnt].b = b; d[cnt].c = c; d[cnt].n = h[a]; h[a] = cnt;
}
void lk(int a, int b, int c){
	cr(a, b, c);
	cr(b, a, 0);
}
int bfs(){
	int i, a, b, c;
	memset(dep, 0, sizeof(dep));
	for(i = 0; i <= tot; i++) cur[i] = h[i];
	queue<int> q;
	q.push(s);
	dep[s] = 1;
	while(!q.empty()){
		a = q.front();
		q.pop();
		for(i = h[a]; i; i = d[i].n){
			b = d[i].b;
			c = d[i].c;
			if(!dep[b] && c){
				dep[b] = dep[a] + 1;
				q.push(b);
			}
		}
	}
	return dep[t];
}
int dfs(int a, int flow){
	int i, b, c, w, used = 0;
	if(a == t) return flow;
	for(i = cur[a]; i; i = d[i].n){
		cur[a] = i;
		b = d[i].b;
		c = d[i].c;
		if(dep[b] == dep[a] + 1 && c > 0){
			if(w = dfs(b, min(flow - used, c))){
				used += w;
				d[i].c -= w;
				d[i ^ 1].c += w;
			}
			if(used == flow) break;
		}
	}
	return used;
}
/*void dfss(int a){ int i, b, c; if(a <= n) printf("%d ", a); else vis[a - n] = 1; for(i = h[a]; i; i = d[i].n){ b = d[i].b; c = d[i].c; if(c > 0) dfss(b); } }*/
int main(){
	int i, j, b, c;
	scanf("%d%d", &n, &m);
	tot = n + m + 2;
	s = n + m + 1, t = n + m + 2;
	for(i = 1; i <= n; i++){
		scanf("%d", &j);
		sum += j;
		lk(s, i, j);
		char tools[10000];
		memset(tools,0,sizeof tools);
		cin.getline(tools,10000);
		int ulen=0,tool;
		while (sscanf(tools+ulen,"%d",&tool)==1)
		{   
			lk(i, tool + n, inf);
			a[i][tool] = 1;
		    if (tool==0) 
		        ulen++;
		    else {
		        while (tool) {
		            tool/=10;
		            ulen++;
		        }
		    }
		    ulen++;
		}
	}
	for(i = 1; i <= m; i++){
		scanf("%d", &j);
		lk(i + n, t, j);
	}
	while(bfs()) ans += dfs(s, inf);
	for(i = 1; i <= n; i++) if(dep[i]) printf("%d ", i);
	printf("\n");
	for(i = 1; i <= m; i++) if(dep[i + n]) printf("%d ", i);
	printf("\n");
	printf("%d", sum - ans);
	return 0;
}