1737: 太空飞行计划问题 SPJ
Time Limit: 1000 MS Memory Limit: 65536 KB
Total Submit: 955 Accepted: 225 Page View: 1877
Submit Status Discuss
Description
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业 性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这 些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集Rj。 配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的 任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才 能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部 费用的差额。 «编程任务: 对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
Input
由文件input.txt提供输入数据。文件第1行有2 个正整数m和n。m是实验数,n是仪 器数。接下来的m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费 用;接着是该实验需要用到的若干仪器的编号。最后一行的n个数是配置每个仪器的费用。
Output
程序运行结束时,将最佳实验方案输出到文件output.txt 中。第1 行是实验编号;第2 行是仪器编号;最后一行是净收益。
2 3 10 1 2 25 2 3 5 6 7
1 2 1 2 3 17
Hint
王晓东《线性规划和网络流24题》系列题目不需要管output.txt,是标准输入输出,多文件单数据。 所有题目中n的值一般小于150.
Source
Tags
题意:m个实验,n个仪器,每个实验需要用到一些仪器,实验带来收益,仪器有成本,求最大净收益。然后输出选择的实验和仪器。
裸的最大权闭合子图 其实一开始我不会,转化为最小割问题。
设源点s,汇点t,s到正权值的点(即实验)建边,边权为权值;负权值的点(即仪器)到t建边,边权为权值的绝对值;正权点到负权点建边,权值为inf。最大权=正权值之和-最小割,证明略。
最大流最小割定理:是指在一个网络流中,能够从源点到达汇点的最大流量等于如果从网络中移除就能够导致网络流中断的边的集合的最小容量和。即在任何网络中,最大流的值等于最小割的容量。
那么 最大权=正权值之和-最大流 直接套模板。
接下来要输出选择的实验和仪器。因为求的是最大流,那么最后一次bfs过程中能走到的点就不在最大流中,而答案是正权值之和-最大流,那剩下的点就是我们选择的实验和仪器。即 判断dis数组的值即可。
忘记初始化wa了n次
#include "bits/stdc++.h"
using namespace std;
const int inf = 0x3f3f3f3f;
struct edge
{
int v,w,nxt;
}g[40000];
int s,t;
int head[1500];
int cur[1500];
int cnt;
int dis[1500];
int n,m;
void addedge(int u,int v,int w)
{
g[cnt].v=v;
g[cnt].w=w;
g[cnt].nxt=head[u];
head[u]=cnt;
++cnt;
}
bool bfs()
{
memset(dis,0,sizeof(dis));
dis[s]=1;
queue<int>q;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=g[i].nxt)
{
int v=g[i].v;
if(dis[v]==0&&g[i].w>0)
{
dis[v]=dis[u]+1;
if(v==t) return 1;
q.push(v);
}
}
}
return dis[t]!=0;
}
int dfs(int u,int flow)
{
if(u==t)return flow;
int ans=0,x=0;
for(int i=cur[u];i!=-1;i=g[i].nxt)
{
int v=g[i].v;
if((dis[v]==dis[u]+1)&&g[i].w>0)
{
x=dfs(v,min(g[i].w,flow-ans));
g[i].w-=x;
g[i^1].w+=x;
if(g[i].w)
cur[u]=i;
ans+=x;
if(ans==flow) return flow;
}
}
if(ans==0) dis[u]=0;
return ans;
}
int dinic()
{
int ans=0;
while(bfs())
{
for(int i=0;i<cnt;i++)
cur[i]=head[i];
ans+=dfs(s,inf);
}
}
int main()
{
int m,n;
char x[100];
memset(head,-1,sizeof(head));
scanf("%d%d",&m,&n);
s=0;
t=m+n+1;
int sum=0;
for(int i=1;i<=m;i++)
{
int w;
scanf("%d",&w);
sum+=w;
addedge(s,i,w);
addedge(i,s,0);
while(1)
{
int w;
char c;
scanf("%d%c",&w,&c);
addedge(i,w+m,inf);
addedge(m+w,i,0);
if(c=='\n'||c=='\r')break;
}
}
for(int i=1;i<=n;i++)
{
int w;
scanf("%d",&w);
addedge(i+m,t,w);
addedge(t,i+m,0);
}
int ans=0;
while(bfs())
{
for(int i=0;i<=cnt;i++)
cur[i]=head[i];
ans+=dfs(s,inf);
}
for(int i=1;i<=m;i++)
{
if(dis[i])
printf("%d ",i);
}
printf("\n");
for(int i=m+1;i<=m+n;i++)
{
if(dis[i])
printf("%d ",i-m);
}
printf("\n");
printf("%d\n",sum-ans);
return 0;
}