luogu P2762 太空飞行计划问题(最大权闭合图)

luogu P2762 太空飞行计划问题(最大权闭合图)

题目大意

W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集RjÍI。配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。

对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。

解题思路

将所有的实验对所需的仪器连边,问题就是求使得后继点也在子图中使得点权最大的点集划分方法.这里需要用到最大权闭合图的知识.所有原本存在的边之间连接容量为无限的边,所有的正权点向源点连接容量为权值的边,所有的负权点向汇点连接容量为权值的绝对值的边.对这个图跑最大流得到其最小割.再将总的最大入流量减最小割即为我们所求的答案.值得一提的是,想要观察最大权闭合图中选择了哪些点,只需要观察其dep是否被更新即可

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
const int maxm=3005;
const int inf=0x3f3f3f3f;
struct Edge{
	int to,nxt,cap,flow;
}edge[maxm];
int tol;
int head[maxn];
void init(){
	tol=2;
	memset(head,-1,sizeof(head));
}
void AddEdge(int u,int v,int w,int rw=0){
	edge[tol].to=v;edge[tol].cap=w;edge[tol].flow=0;
	edge[tol].nxt=head[u];head[u]=tol++;
	edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=0;
	edge[tol].nxt=head[v];head[v]=tol++;
}
int Q[maxn];
int dep[maxn],cur[maxn],sta[maxn];
bool bfs(int s,int t,int n){
	int front=0,tail=0;
	memset(dep,-1,sizeof(dep[0])*(n+1));
	dep[s]=0;
	Q[tail++]=s;
	while(front<tail){
		int u=Q[front++];
		for(int i=head[u];i!=-1;i=edge[i].nxt){
			int v=edge[i].to;
			if(edge[i].cap>edge[i].flow&&dep[v]==-1){
				dep[v]=dep[u]+1;
				if(v==t) return true;
				Q[tail++]=v;
			}
		}
	}
	return false;
}
int dinic(int s,int t,int n){
	int maxflow=0;
	while(bfs(s,t,n)){
		for(int i=0;i<n;i++) cur[i]=head[i];
		int u=s,tail=0;
		while(cur[s]!=-1){
			if(u==t){
				int tp=inf;
				for(int i=tail-1;i>=0;i--)
				{
					tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow);
				}
				maxflow+=tp;
				for(int i=tail-1;i>=0;i--){
					edge[sta[i]].flow+=tp;
					edge[sta[i]^1].flow-=tp;
					if(edge[sta[i]].cap-edge[sta[i]].flow==0) tail=i;
				}
				u=edge[sta[tail]^1].to;
			}
			else if(cur[u]!=-1&&edge[cur[u]].cap>edge[cur[u]].flow&&dep[u]+1==dep[edge[cur[u]].to]){
				sta[tail++]=cur[u];
				u=edge[cur[u]].to;
			}
			else{
				while(u!=s&&cur[u]==-1) u=edge[sta[--tail]^1].to;
				cur[u] = edge [cur[u]].nxt;
			}
		}
	}
	return maxflow;
}
char tools[10000];
int ulen,tool;
vector<int> ansv;set<int> ansq;
vector<int> nd[55];
int main()
{
	int n,m;
	cin>>m>>n;
	int w;
	int s=0,t=n+m+1;
	init();
	int sum=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&w);
		sum+=w; 
		AddEdge(s,i,w);
		ulen=0;
		memset(tools,0,sizeof(tools));
		cin.getline(tools,10000);
		while(sscanf(tools+ulen,"%d",&tool)==1)
		{
			AddEdge(i,m+tool,inf);
			nd[i].push_back(tool);
			if(tool==0) ulen++;
			else{
				while(tool){tool/=10;ulen++;}
			}
			ulen++;
		}
	}
	for(int i=1;i<=n;i++) scanf("%d",&w),AddEdge(m+i,t,w);
	int ans=dinic(s,t,n+m+2);
	for(int i=1;i<=m;i++) if(dep[i]!=-1) ansv.push_back(i);
	for(auto x:ansv) for(auto y:nd[x]) ansq.insert(y);
	for(auto x:ansv) cout<<x<<' ';cout<<endl;
	for(auto x:ansq) cout<<x<<' ';cout<<endl;
	cout<<sum-ans<<endl;
}
	

猜你喜欢

转载自blog.csdn.net/baiyifeifei/article/details/88024342