太空飞行计划问题

首先诚挚感谢BYvoid大神博客里提供的好题和数据

此为线性规划与网络流24题的第二题。

自己没想出来,看byvoid写的大致思路的时候我是一脸“还有这种操作”的表情Orz。

«问题描述:
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业
性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这
些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集RjÍI。
配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的
任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才
能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部
费用的差额。
«编程任务:
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
«数据输入:
由文件input.txt提供输入数据。文件第1行有2 个正整数m和n。m是实验数,n是仪
器数。接下来的m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费
用;接着是该实验需要用到的若干仪器的编号。最后一行的n个数是配置每个仪器的费用。
«结果输出:
程序运行结束时,将最佳实验方案输出到文件output.txt 中。第1 行是实验编号;第2
行是仪器编号;最后一行是净收益。

 

直接贴BYvoid大神留下的思路好了。

【问题分析】

最大权闭合图问题,可以转化成最小割问题,进而用最大流解决。

【建模方法】

把每个实验看作二分图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最小,即为最小割。

该问题的一般模型为最大权闭合图,相关讨论见《最小割模型在信息学竞赛中的应用》作者胡伯涛。

 

//诚挚感谢byvoid大神提供的题目和数据
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<iostream>
#define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int MAXN=10010;
int tmp,m,n,S,T,Ans;
int tot=0,f=0,ans=0,Maxflow=0;
int h[MAXN];
int pointer[MAXN];
int a[MAXN],path[MAXN];
const int INF=~0U>>1;
struct edge{
    int next,to;
    int cap,op;                 //op指向反向边 cap为容量 f为流量
    int f;
}edge[MAXN*100];
void addedge(int a,int b,int c)  //a到b容量为c
{
    edge[tot].to=b;
    edge[tot].next=pointer[a];
    edge[tot].cap=c;
    pointer[a]=tot;
    edge[tot].op=tot^1;
    edge[tot].f=0;
    tot++;
    edge[tot].to=a;                     //建立b到a的反向边
    edge[tot].next=pointer[b];
    edge[tot].cap=0;
    pointer[b]=tot;
    edge[tot].op=tot^1;
    edge[tot].f=0;
    tot++;
}
void Input()
{
    memset(pointer,-1,sizeof(pointer));
    scanf("%d%d",&m,&n);
    T=m+n+1;S=0;
    int a,c;
    rep(i,1,m)
    {
        scanf("%d",&c);
        addedge(S,i,c);
        ans+=c;
        for (;;)
        {
            do c=getchar(); while(c==' ');
            ungetc(c,stdin);
            if (c==10 || c==13) break;
            scanf("%d",&a);
            addedge(i,a+m,INF);
        }
    }
    rep(j,1,n)
    {
        scanf("%d",&c);
        addedge(j+m,T,c);
    }
}
bool Dinic_restruct()          //bfs建立层次图
{
    queue <int>q;
    memset(h,-1,sizeof(h));
    h[S]=0;q.push(S);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=pointer[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(h[v]==-1&&edge[i].cap-edge[i].f)
            {
                h[v]=h[u]+1;
                q.push(v);
             //   printf("v=%d T=%d u=%d\n",v,T,u);
                if(v==T) return true;
            }
        }
    }
    return false;
}
void Dinic_augment()     //dfs找最短增光路
{
    int i,j,f,Stop=1;
    a[Stop]=S;
    while(Stop)
    {
        i=a[Stop];
     //   printf("augment i=%d\n",i);
        if(i!=T)       //没到T就走一步,dfs
        {
            int v;
            for(j=pointer[i];j!=-1;j=edge[j].next)
                {
                    v=edge[j].to;
                    if(h[v]==h[i]+1&&edge[j].cap-edge[j].f) break;
                }
            if(j!=-1)
            {
                a[++Stop]=v;
                path[Stop]=j;
            }
            else            //回退
            {

                Stop--;
                h[i]=-1;
               // printf("-1 augment i=%d\n",i);
            }
        }
        else    //找到了一条完整的路径
        {
            f=INF;
            for(i=Stop;i>=2;i--)
            {
                int &t=path[i];
                if(edge[t].cap-edge[t].f<f)
                    f=edge[t].cap-edge[t].f;
            }
            Maxflow+=f;
            for(int i=Stop;i>=2;i--)
            {
                int &t=path[i];
                edge[t].f+=f;
                edge[t^1].f-=f;    //反向边
                if(edge[t].cap-edge[t].f==0) Stop=i-1;
            }
        }
    }
}
void Dinic()
{
    while(Dinic_restruct())
        Dinic_augment();

}
void Pout()             //高度为-1代表在最后一次建图时从S连接不到这个点,赚钱的实验组必然有至少一个实验到S的路径是未堵塞的
{                                                   //从而这个实验组包含的实验和所需仪器都与S是连通的
    rep(i,1,m) if(h[i]!=-1) printf("%d ",i);
    printf("\n");
    rep(i,m+1,m+n) if(h[i]!=-1) printf("%d ",i-m);
    printf("\n%d\n",ans-Maxflow);
}
int main()
{
   // freopen("shut2.in","r",stdin);
    Input();
    Dinic();
    Pout();
    return 0;
}

 

posted on 2017-06-11 14:35  缄默火  阅读(657)  评论(4编辑  收藏  举报

导航