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

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
#include
#include
#include
#include
using namespace std;
const int N=10010;
const int inf=0x3f3f3f3f;
queue<int> Q;
int S,T,n,m,head[N],to[2*N],w[2*N],nxt[2*N],num=1,sum=0,dep[N];
bool vis[N];
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;
}

你可能感兴趣的:(&,图论,网络流,最大权闭合子图,题解)