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

标签:最小割

听说这玩意叫最大权闭合图?

Solution

观察原条件“选择实验 \(E_i\),必须已经配置仪器 \(I_{j \in R_i}\)”,是一对多的约束条件,比较难处理。
考虑如何转化成一对一的条件,发现可以将其转化为“不选择 \(I_{j \in R_i}\) 就不能选择 \(E_i\)”。
如果将不选看成两个集合,那么题目变成了一个经典二选一问题,可以用最小割处理。

考虑所有实验都选择,所有器材都不选择,此时利润为 \(Sum\)。此时情况是矛盾的,需要使用最小的代价使其合法。
设与源点 \(s\) 相连的集合 \(S\) 表示不选,与汇点 \(t\) 相连的集合 \(T\) 表示
那么连接源点 \(s\)\(I_i\),将其容量设置为 \(c_i\),表示选择 \(I_i\) 需要花费 \(c_i\)
连接 \(E_i\) 与 汇点 \(t\),将其容量设置为 \(p_j\),表示不选择这个实验会损失 \(p_i\) 利润;
连接 \(I_{j \in R_i}\)\(E_i\) ,将其容量设置为 \(inf\),表示不选 \(I_{j \in R_i}\) 就不能选择 \(E_i\)
最后跑一边最大流,得到最小割 \(Flow\)。那么最终答案即为 \(Sum-Flow\)

关于构造答案,从源点 \(s\) 出发沿着残留网络遍历到位于集合 \(S\) 的点,剩余的点皆为位于 \(T\) 的点。如此就可以知道有哪些实验与器材被选择。

Code

#include<bits/stdc++.h>
#define M 5005
#define N 205
using namespace std;
typedef long long ll;
typedef double db;
char IO;
int rd(){
	int num=0;bool f=0;
	while(IO=getchar(),IO<48||IO>57)if(IO=='-')f=1;
	do num=(num<<1)+(num<<3)+(IO^48);
	while(IO=getchar(),IO>=48&&IO<=57);
	return f?-num:num;
}
int to[M],nxt[M],val[M],hd[N],cnte=1;
void Adde(int u,int v,int w){
	to[++cnte]=v;val[cnte]=w;
	nxt[cnte]=hd[u];hd[u]=cnte;
}
void Add(int u,int v,int w){
	Adde(u,v,w);Adde(v,u,0);
}
int n,m,s,t,ans;
int dep[N],cur[N];
queue<int> Q;
int BFS(){
	memset(dep,0,sizeof(dep));
	memcpy(cur,hd,sizeof(cur));
	dep[s]=1;Q.push(s);
	int u,v;
	while(!Q.empty()){
		u=Q.front();Q.pop();
		for(int i=hd[u];i;i=nxt[i]){
			if(!dep[v=to[i]]&&val[i])
				dep[v]=dep[u]+1,Q.push(v);
		}
	}
	return dep[t];
}
int DFS(int u,int flow){
	if(u==t)return flow;
	int f,res=0;
	for(int i=cur[u],v;i&&flow;i=nxt[i]){
		cur[u]=i;v=to[i];
		if(dep[v]!=dep[u]+1)continue;
		if(val[i]&&(f=DFS(v,min(flow,val[i])))>0){
			val[i]-=f;
			val[i^1]+=f;
			res+=f;flow-=f;
		}
	}
	return res;
}
bool Set[N];
void Find(int s){
	Set[s]=1;
	for(int i=hd[s],y;i;i=nxt[i]){
		if(!val[i]||Set[y=to[i]])continue;
		Find(y);
	}
}
int A[N],B[N];
int main(){
	n=rd(),m=rd();
	s=0,t=n+m+1;
	for(int i=m+1,x;i<=m+n;++i){
		Add(i,t,x=rd());ans+=x;
		while(IO!='\r'&&IO!='\n')
			Add(rd(),i,1e9);
	}
	for(int i=1;i<=m;++i)
		Add(s,i,rd());
	while(BFS())
		ans-=DFS(s,1e9);
	Find(s);
	for(int i=m+1;i<=m+n;++i)
		if(!Set[i])printf("%d ",i-m);
	puts("");
	for(int i=1;i<=m;++i)
		if(!Set[i])printf("%d ",i);
	puts("");
	cout<<ans;
	return 0;
}
posted @ 2021-07-18 14:01  Alnorie  阅读(55)  评论(0)    收藏  举报