【网络流24题】 太空飞行计划问题

题面

分析

这类题叫最大权闭合图问题。
有向图的闭合图是指每个点的后驱节点都在图中。
这道题中,一些实验和它对应的仪器形成了个闭合二分图,一边点权是正,一边是负,总收益是权值和。
怎么最大化权值和呢?
建超级源点 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;
} 
各种题解及学习笔记~ 文章被收录于专栏

各种题解及学习笔记

全部评论

相关推荐

点赞 评论 收藏
分享
面试官问:为什么不考研?该怎么回答啊😭我说现在的就业环境差到底了,还有就是我不想学数学,感觉面试官笑容都凝固了😢
DayDayNoBug的鲜芋球:我说的是“上学期其实尝试过去探索一些研究的方向,但感觉那些对我来说都没有很大的吸引力,相比起研究我可能更喜欢开发这种实践性的东西,它会让我觉得很有意思并且会为之深入进去”(虽然也不知这个回答怎么样哈哈哈哈哈哈)
点赞 评论 收藏
分享
震撼沃玛一整年:查看图片
点赞 评论 收藏
分享
大方的大熊猫准备进厂:1.教育背景:你希望从事什么专业的工作你的主修课就是什么;成绩优秀是你应该做的,没什么可描述的,成绩不优秀也许人家在大学忙着创业呢?(成绩优秀不一定是好事,只能说明多元化的大学你上成了高中,没有真正上明白大学,反而体现了你死板,不爱社交,没有别的突出能力) 2.实践经历:你想表达的意思没有说清楚。你是说你会个性化服务,还是你有实习经历。如果没有带来,经济收益,表彰,更好的发展前景,那你还不如说说提升了自己哪些技能。你说有人给你送锦旗我都能明白你优秀,但是你说你会xxxx,你说这话谁信,证据呢。 3.入伍经历:你描述的就是你的工作职责或者你应该做的,并没有体现出来你把这个事情做好了,而且入伍经历并不能证明你能干好你要应聘的工作,不如只写经历其余所有内容都不写。 4.荣誉技能:重点突出一下,但不要过多描述,这些荣誉的含金量懂得都懂。 重点:你要应聘什么工作(具体岗位,实习生不具体),你的期望薪资
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务