727. [网络流24题] 太空飞行计划
★★☆ 输入文件:shuttle.in
输出文件:
shuttle.out
简单对比
时间限制:1 s 内存限制:128 MB
【问题描述】
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合 E ={ E 1 , E 2 ,…, E m },和进行这些实验需要使用的全部仪器的集合 I ={ I 1 , I 2 ,…, I n }。实验 Ej 需要用到的仪器是 I 的子集 R j ∈ I 。配置仪器 I k 的费用为 c k 美元。实验 E j 的赞助商已同意为该实验结果支付 p j 美元。W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。【编程任务】
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。【数据输入】
第1行有2个正整数m和n(m,n <= 100)。m是实验数,n是仪器数。接下来的m行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。最后一行的n个数是配置每个仪器的费用。【结果输出】
第1行是实验编号;第2行是仪器编号;最后一行是净收益。【输入文件示例】shuttle.in
2 3 10 1 2 25 2 3 5 6 7
【输出文件示例】shuttle.out
1 2 1 2 3 17
【问题分析】
最大权闭合图问题,可以转化成最小割问题,进而用最大流解决。
【建模方法】
把每个实验看作二分图X集合中的顶点,每个设备看作二分图Y集合中的顶点,增加源S和汇T。
1、从S向每个Xi连接一条容量为该点收入的有向边。
2、从Yi向T连接一条容量为该点支出的有向边。
3、如果一个实验i需要设备j,连接一条从Xi到Yj容量为无穷大的有向边。
统计出所有实验的收入只和Total,求网络最大流Maxflow,最大收益就是Total-Maxflow。对应的解就是最小割划分出的S集合中的点,也就是最后一次增广找到阻塞流时能从S访问到的顶点。
【建模分析】
定义一个割划分出的S集合为一个解,那么割集的容量之和就是(未被选的A集合中的顶点的权值 + 被选的B集合中的顶点的权值),记为Cut。A集合中所有顶点的权值之和记为Total,那么Total - Cut就是(被选的A集合中的顶点的权值 - 被选的B集合中的顶点的权值),即为我们的目标函数,记为A。要想最大化目标函数A,就要尽可能使Cut小,Total是固定值,所以目标函数A取得最大值的时候,Cut最小,即为最小割。
该问题的一般模型为最大权闭合图,相关讨论见《最小割模型在信息学竞赛中的应用》作者胡伯涛。
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<iomanip>
//#define mem(dp,a) memset(dp,a,sizeof(dp))
//#define fo(i,n) for(int i=0;i<(n);i++)
//#define INF 0x3f3f3f3f
#define fread() freopen("data.txt","r",stdin)
#define fwrite() freopen("out.out","w",stdout)
using namespace std;
typedef long long ll;
vector<int> re;
int s,t;
int m,n,t1,t2;
int ans;
const int MAXN=100000,MAXM=100000,inf=1e9;
struct Edge
{
int v,c,f,nx;
Edge() {}
Edge(int v,int c,int f,int nx):v(v),c(c),f(f),nx(nx) {}
} E[MAXM];
int G[MAXN],cur[MAXN],pre[MAXN],dis[MAXN],gap[MAXN],N,sz;
void init(int _n) //初始化
{
N=_n,sz=0; memset(G,-1,sizeof(G[0])*N);
}
void link(int u,int v,int c)//连接两个点
{
E[sz]=Edge(v,c,0,G[u]); G[u]=sz++;
E[sz]=Edge(u,0,0,G[v]); G[v]=sz++;
}
int ISAP(int S,int T)
{//S -> T
int maxflow=0,aug=inf,flag=false,u,v;
for (int i=0;i<N;++i)cur[i]=G[i],gap[i]=dis[i]=0;
for (gap[S]=N,u=pre[S]=S;dis[S]<N;flag=false)
{
for (int &it=cur[u];~it;it=E[it].nx)
{
if (E[it].c>E[it].f&&dis[u]==dis[v=E[it].v]+1)
{
if (aug>E[it].c-E[it].f) aug=E[it].c-E[it].f;
pre[v]=u,u=v; flag=true;
if (u==T)
{
for (maxflow+=aug;u!=S;)
{
E[cur[u=pre[u]]].f+=aug;
E[cur[u]^1].f-=aug;
}
aug=inf;
}
break;
}
}
if (flag) continue;
int mx=N;
for (int it=G[u];~it;it=E[it].nx)
{
if (E[it].c>E[it].f&&dis[E[it].v]<mx)
{
mx=dis[E[it].v]; cur[u]=it;
}
}
if ((--gap[dis[u]])==0) break;
++gap[dis[u]=mx+1]; u=pre[u];
}
return maxflow;
}
bool bfs(int S,int T)
{
static int Q[MAXN];
memset(dis,-1,sizeof(dis[0])*N);
dis[S]=0;
Q[0]=S;
for (int h=0,t=1,u,v,it ; h<t ; ++h)
{
for ( u=Q[h],it=G[u] ; ~it ; it=E[it].nx)
{
if (dis[v=E[it].v]==-1 && E[it].c > E[it].f)
{
dis[v]=dis[u]+1;
Q[t++]=v;
}
}
}
return dis[T]!=-1;
}
int dfs(int u,int T,int low)
{
if (u==T) return low;
int ret=0,tmp,v;
for (int &it=cur[u];~it&&ret<low;it=E[it].nx)
{
if (dis[v=E[it].v]==dis[u]+1&&E[it].c>E[it].f)
{
if (tmp=dfs(v,T,min(low-ret,E[it].c-E[it].f)))
{
ret+=tmp;
E[it].f+=tmp;
E[it^1].f-=tmp;
// low -= tmp;
// if(low==0)break;
}
}
}
if (!ret) dis[u]=-1;
return ret;
}
int dinic(int S,int T)
{
int maxflow=0,tmp;
while (bfs(S,T))
{
memcpy(cur,G,sizeof(G[0])*N);
while (tmp=dfs(S,T,inf)) maxflow+=tmp;
}
return maxflow;
}
int main()
{
ios_base::sync_with_stdio(false);
// fread();
freopen("shuttle.in","r",stdin);
freopen("shuttle.out","w",stdout);
scanf("%d%d",&m,&n);
s=0; t=n+m+1;
init(t+1);
for (int i=1;i<=m;i++)
{
scanf("%d",&t1);
link(s,i,t1);
ans += t1;
for (;;)
{
do t1=getchar(); while (t1==' '); ungetc(t1,stdin);
if (t1==10 || t1==13) break;
scanf("%d",&t2);
link(i,t2+m,inf);
}
}
for (int i=1;i<=n;i++)
{
scanf("%d",&t1);
link(i+m,t,t1);
}
int tmp_ans = dinic(s,t);;
// for(int i = 0;i <=m+n;i++)
// {
// cout << dis[i]<<" ";
// }
// cout <<endl;
for(int i = 1;i <= m;++i)
if(dis[i]!=-1)
cout<<i<<" ";
cout<<endl;
for(int i = m+1;i <= m+n;++i)
if(dis[i]!=-1)
cout << i-m<<" ";
cout <<endl;
cout << ans - tmp_ans <<endl;
return 0;
}