洛谷 P2762 太空飞行计划问题 (网络流24题)

题目:太空飞行计划问题

思路:
把所有的实验和s连边,权值为实验的费用;
把所有的材料和t连边,权值为材料的费用;
再把实验和对应的材料连边,权值为inf。
求一遍最小割,输出下路径。
注意一下,最小割的路径和最大流不同。
一条边被割裂的条件,是一端与s相连,另一端不与s相连,即d值一个为0,一个非0。

update(2021/10/28):
求出最小割后,全图被分为了两部分,一部分节点和S相连,代表选,另一部分和T相连,代表不选。
而和S相连的实验被割掉代表这个实验不被选择,和T相连的器材被割掉代表这个器材被选择。
答案=总实验经费 - 不选的实验费用 - 选的器材费用 = 最小割。

代码:

#include<bits/stdc++.h>
using namespace std;

#define maxn 10000
#define inf ((int)1e9)

struct Edge{
	int u,v,w;
	Edge(){}
	Edge(int uu,int vv,int ww) {u=uu,v=vv,w=ww;}
};

int n,m;

int sum=0;

Edge e[maxn+5];
int nxt[maxn+5],g[maxn+5],cnt=-1;	//邻接表存边 

int cur[maxn+5];	//cur 当前弧优化 
int d[maxn+5];	//分层图 

void add(int u,int v,int w) {
	e[++cnt]=Edge(u,v,w);
	nxt[cnt]=g[u];
	g[u]=cnt;
} 

void readin() {	//建图 
	memset(g,-1,sizeof(g));
	memset(nxt,-1,sizeof(nxt));

	char c[maxn+5];
	scanf("%d%d",&n,&m);
	for (int i=1; i<=n; i++) {
		int v;
		scanf("%d",&v);
		sum+=v;
		add(0,i,v);
		add(i,0,0);
		memset(c,0,sizeof(c));
		cin.getline(c,maxn+5);
		int ulen=0,num;
		while (sscanf(c+ulen,"%d",&num)==1) {
			add(i,n+num,inf);
			add(n+num,i,0);
			if (num==0) ulen++;
			else while (num) {
					num/=10;
					ulen++;
				}
			ulen++;
		}
	}
	for(int i=1;i<=m;i++) {
		int v;
		scanf("%d",&v);
		add(i+n,maxn,v);
		add(maxn,i+n,0);
	}
}

queue<int> que;

bool bfs() {	//分层图 
	memset(d,0,sizeof(d));
	d[0]=1;
	que.push(0);
	while(!que.empty()) {
		int h=que.front();que.pop();
		for(int i=g[h];~i;i=nxt[i]) {
			Edge y=e[i];
			if(y.w==0||d[y.v]) continue;
			d[y.v]=d[h]+1;
			que.push(y.v);
		}		
	}
	
	if(d[maxn]>0) return true;
	else return false;
}

int dfs(int x,int w) {	//增广 
	if(x==maxn) return w;
	for(int&  i=cur[x]; ~i;i=nxt[i]) {
		Edge y=e[i];
		if(d[y.v]-d[x]!=1||!y.w) continue;
		int z=dfs(y.v,min(w,y.w));
		if(z) {
			e[i].w-=z;
			e[i^1].w+=z;
			return z;
		}
	}
	return 0;
}

int slv() {
	int ans=0;
	while(bfs()) {
		for(int i=0;i<=m+n;i++) cur[i]=g[i];cur[maxn]=maxn;
		while(int x=dfs(0,inf)) ans+=x;
	}
	return ans;
}

int p[maxn+5],q[maxn+5];

void print() {
	for(int i=1;i<=n;i++) if(d[i]) printf("%d ",i);printf("\n");
	for(int j=1;j<=m;j++) if(d[j+n]) printf("%d ",j);printf("\n");
}

int main() {
	readin();
	int ans=slv();
	print();
	printf("%d\n",sum-ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值