nefu476最大权闭包图(网络流)

一、太空飞行计划问题
Time Limit 1000 ms
Memory Limit 65536 kb
description
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可
供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej 需要用到的仪
器是I 的子集。配置仪器Ik 的费用为ck 美元。实验Ej 的赞助商已同意为该实验结果支付pj 美元。W 教授的任务是找出一个有
效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验
所获得的全部收入与配置仪器的全部费用的差额。
input
多组数据输入.
每组输入第1行有2 个正整数m 和n。m 是实验数,n 是仪器数。接下来的m 行,每行是一个实验的有关数据。第一个数赞助商同
东北林业大学信息与计算机科学学院黄泽群IX
意支付该实验的费用;接下来是该实验需要的仪器数;接着是对应仪器的编号。最后一行的n 个数是配置每个仪器的费用。
output
每组输出最佳实验方案的净收益
sample_input
2 3
10 2 1 2
25 2 2 3
5 6 7
sample_output
17
分析: 定义:一个有向图的闭合图G=(V,E)是该有向图的一个点集,且该点集的所有出边都还指向该点集。即闭合图内的任意点的任意后继也一定在闭合图中。

给每个点v分配一个点权(任意实数,可正可负)。最大权闭合图,是一个点权之和最大的闭合图。

bihetu.JPG  

此图中有9个闭合图:空集,{3,4,5},{4,5},{5},{2,4,5},{2,5},{2,3,4,5},{1,2,4,5},{1,2,3,4,5}。最大权闭合图是{3,4,5},权和为4。

闭合图的性质恰好反映了事件之间的必要条件的关系:一个事件发生,它需要的所有前提都要发生。

下面通过构图,我们将最大权闭合图问题转化成最小割问题,即最大流问题求解。

定义W[I]代表顶点I的权值,新增源点S,汇点T。

1、若W[I]>0,则S向I连一条容量为W[I]的边。
2、若W[I]<0,则I向T连一条容量为-W[I]的边。
3、原图中的边,容量设置为正无穷。

这样,最小割就对应了最大权闭合图,而总盈利-最大流就是权和。

#include <iostream>
#include <cstdio>
using namespace std;
const int oo=1e9;
const int mn=999;
const int mm=111111;
int node,src,dest,edge;
int ver[mm],next[mm],flow[mm];
int head[mn],work[mn],dis[mn],q[mn];
void prepare(int _node,int _src,int _dest)
{
    node=_node,src=_src,dest=_dest;
    for(int i=0;i<node;i++) head[i]=-1;
    edge=0;
}
void addedge(int u,int v,int c)
{
    ver[edge]=v;flow[edge]=c;next[edge]=head[u];head[u]=edge++;
    ver[edge]=u;flow[edge]=0;next[edge]=head[v];head[v]=edge++;
}
bool Dinic_bfs()
{
    int i,u,v,l,r=0;
    for(i=0;i<node;i++)
        dis[i]=-1;
    dis[q[r++]=src]=0;
    for(l=0;l<r;l++)
        for(i=head[u=q[l]];i>=0;i=next[i])
            if(flow[i]&&dis[v=ver[i]]<0)
            {
                dis[q[r++]=v]=dis[u]+1;
                if(v==dest) return 1;
            }
    return 0;
}
int Dinic_dfs(int u,int exp)
{
    if(u==dest) return exp;
    for(int &i=work[u],v,tmp;i>=0;i=next[i])
    {
        if(flow[i]&&dis[v=ver[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0)
        {
            flow[i]-=tmp;
            flow[i^1]+=tmp;
            return tmp;
        }
    }
    return 0;
}
int Dinic_flow()
{
    int i,ret=0,delta;
    while(Dinic_bfs())
    {
        for(i=0;i<node;i++)
            work[i]=head[i];
        while(delta=Dinic_dfs(src,oo))  ret+=delta;
    }
    return ret;
}
int main()
{
    int m,n,sum,nn,i,v,u,summ,ans,ans1,ans2;
    while(~scanf("%d%d",&m,&n))
    {
        prepare(n+m+2,0,n+m+1);
        ans1=0;
        for(u=1;u<=m;u++)
        {
            scanf("%d",&sum);
            ans1+=sum;
            addedge(0,u,sum);
            scanf("%d",&nn);
            while(nn--)
            {
                scanf("%d",&v);
                addedge(u,m+v,oo);
            }
        }
        for(u=m+1;u<=n+m;u++)
        {
            scanf("%d",&summ);
            addedge(u,n+m+1,summ);
        }
        ans2=Dinic_flow();
       // cout<<"ans2="<<ans2<<endl;
        ans=ans1-ans2;
        /*for(i=1;i<=m;i++)
            if(dis[i]!=-1)  printf("%d ",i);
            printf("\n");
        for(i=m+1;i<=n+m;i++)
            if(dis[i]!=-1)  printf("%d ",i);*/

        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值