• 太空飞行计划问题


    首先诚挚感谢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
    ",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
    ",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
    ",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("
    ");
        rep(i,m+1,m+n) if(h[i]!=-1) printf("%d ",i-m);
        printf("
    %d
    ",ans-Maxflow);
    }
    int main()
    {
       // freopen("shut2.in","r",stdin);
        Input();
        Dinic();
        Pout();
        return 0;
    }
  • 相关阅读:
    Socket开发框架之消息的回调处理
    Socket开发框架之数据加密及完整性检查
    Socket开发框架之数据传输协议
    Socket开发框架之框架设计及分析
    C#进行Visio二次开发之文件导出及另存Web页面
    Winform混合式开发框架的特点总结
    代码生成工具Database2Sharp中增加视图的代码生成以及主从表界面生成功能
    基于Metronic的Bootstrap开发框架经验总结(9)--实现Web页面内容的打印预览和保存操作
    基于C#的MongoDB数据库开发应用(4)--Redis的安装及使用
    基于C#的MongoDB数据库开发应用(3)--MongoDB数据库的C#开发之异步接口
  • 原文地址:https://www.cnblogs.com/zhixingr/p/6985149.html
Copyright © 2020-2023  润新知