您的位置:首页 > 理论基础 > 计算机网络

PowerOJ 1737 网络流24题之二 太空飞行计划问题(最大权闭合子图)

2017-12-16 23:39 369 查看

PowerOJ 1737 网络流24题之二 太空飞行计划问题

原题地址

https://www.oj.swust.edu.cn/problem/show/1737

题意:

W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业

性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这

些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集Rj。

配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的

任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才

能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部

费用的差额。

«编程任务:

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

数据范围

王晓东《线性规划和网络流24题》系列题目不需要管output.txt,是标准输入输出,多文件单数据。

所有题目中n的值一般小于150.

题解:

一篇最大权闭合子图博客

S向正边权点连边,权值为该点点权,负权点向T连边,权值为该点点权绝对值。正点向负点的边照连。

于是我们先把答案加上所有的正点点权和,然后跑最小割,答案减去最小割。

考虑其意义:

割掉一条S->u的边,表示不选这个实验,(减去其值)

割掉一条v->T的边,表示选择这个仪器,(减去其值)

一个实验,要么不选它,要么选择它(它的所有仪器)

于是是对应的。

输出方案就是残余图上与S联通的点(选择的),于是通过最后一次bfs的vis判断即可。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=10010;
const int inf=0x3f3f3f3f;
queue<int> Q;
int S,T,n,m,head
,to[2*N],w[2*N],nxt[2*N],num=1,sum=0,dep
;
bool vis
;
void build(int u,int v,int ww)
{
num++;
to[num]=v; nxt[num]=head[u]; w[num]=ww; head[u]=num;
num++;
to[num]=u; nxt[num]=head[v]; w[num]=0; head[v]=num;
}
bool bfs()
{
while(!Q.empty()) Q.pop();
memset(vis,0,sizeof(vis));
Q.push(S); vis[S]=1; dep[S]=1;
while(!Q.empty())
{
int u=Q.front(); Q.pop();
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(vis[v]||w[i]<=0) continue;
dep[v]=dep[u]+1;
vis[v]=1; Q.push(v);
}
}
return vis[T];
}
int dfs(int u,int d)
{
if(u==T||d==0) return d;
int ret=0;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(dep[v]!=dep[u]+1||w[i]<=0) continue;
int flow=dfs(v,min(d,w[i]));
d-=flow; ret+=flow;
w[i]-=flow; w[i^1]+=flow;
if(d==0) break;
}
if(ret==0) dep[u]=-1;
return ret;
}
int main()
{
scanf("%d%d",&n,&m);
S=n+m+1; T=n+m+2;
for(int i=1;i<=n;i++)
{
int c; scanf("%d",&c);
build(S,i,c); sum+=c;
while(getchar()==' ')
{
int x;scanf("%d",&x);
build(i,n+x,inf);
}
}
for(int i=1;i<=m;i++)
{
int c;scanf("%d",&c);
build(n+i,T,c);
}
while(bfs()) sum-=dfs(S,inf);
for(int i=1;i<=n;i++) if(vis[i]) printf("%d ",i); puts("");
for(int i=n+1;i<=n+m;i++) if(vis[i]) printf("%d ",i-n); puts("");
printf("%d\n",sum);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐