【网络流24题】太空飞行计划问题

Luogu P2762

题意

给出m个实验,给出每个实验需要哪些仪器的编号和完成该实验的收益。给出每个仪器的花费。每个仪器都可以在多次使用。求最大收益。并输出具体方案(完成了哪些实验,用了哪些仪器)

题解

不能贪心地解决,可能A实验配置仪器后是亏的,但是配置完仪器后其他的实验要配的仪器变少了,总的收益是更大的。

那么解决这一类A事件的发生和B事件是否发生存在依赖关系,且每个事件都有一定收益(正收益或负收益)的问题,可以用网络流中一个叫做最大权闭合图的模型来解决。推荐一篇论文算法合集之《最小割模型在信息学竞赛中的应用》,胡伯涛大佬写的,其中有对该模型的详细讲解,写的非常好。这篇论文也加深了我对网络流问题的理解。

那么这类问题怎么解决,具体的证明过程看论文,这里只给出建图方法。

原来事件之间的依赖关系对应建一条流量为inf的边,超级源点向每个正收益的事件连一条流量为该事件收益的边,每个亏损的事件向超级汇点连一条流量为亏损的边。跑出的最大流即最小割是总体的最小亏损,拿所有正收益的事件求和,再减去这个最小亏损即是最大收益。

那么还有一个问题,怎么求出方案。这里简单讲一下方法和大致原理。跑完最大流后搜一下残量网络,能与起点联通的事件都选上。原理大致是最小割割掉的那些事件全部舍弃,剩下的就都是需要的事件。

对应到这道题目,做实验是一个正收益的事件,但它依赖于配置仪器这些负收益的事件,那么这样就可以套用上述模型解决了。

代码

直接粘的上古代码......

#include<bits/stdc++.h>
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX 233
using namespace std;

int n,m,ANS=0,T;
int Du[MAX];
vector <int> f[MAX],flow[MAX],FB[MAX];
int dis[MAX];
int dl[2333];

bool BFS()
{
    int t=0,w=1,x,X;
    memset(dis,0xff,sizeof(dis));
    dl[1]=0;dis[0]=0;
    do
    {
        x=dl[++t];
        for(int i=0;i<Du[x];i++)
        {
            X=f[x][i];
            if(dis[X]>=0||!flow[x][i]) continue;
            dis[X]=dis[x]+1;
            dl[++w]=X;
        }
    }while(t!=w);
    if(dis[T]>0) return true;
    return false;
}
int DFS(int x,int MFLOW)
{
    if(x==T) return MFLOW;
    int X,h;
    for(int i=0;i<Du[x];i++)
    {
        X=f[x][i];
        if(dis[X]==dis[x]+1&&flow[x][i]>0&&(h=DFS(X,MIN(flow[x][i],MFLOW))))
        {
            flow[x][i]-=h;
            flow[X][FB[x][i]]+=h;
            return h;
        }
    }
    return 0;
}

void readin();
void work();
void print();

int main()
{
    readin();
    work();
    print();
    return 0;
}

void readin()
{
    int x;
    char c;
    scanf("%d%d",&n,&m);
    T=n+m+1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);getchar();ANS+=x;
        Du[0]++;Du[i]++;
        f[0].push_back(i);flow[0].push_back(x);FB[0].push_back(Du[i]-1);
        f[i].push_back(0);flow[i].push_back(0);FB[i].push_back(Du[0]-1);
        while(1)
        {
            scanf("%d",&x);x+=n;
            c=getchar();
            Du[i]++;Du[x]++;
            f[i].push_back(x);flow[i].push_back(0x7fffffff);FB[i].push_back(Du[x]-1);
            f[x].push_back(i);flow[x].push_back(0);FB[x].push_back(Du[i]-1);
            if(c==13||c==10) break;
        }
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&x);
        Du[i+n]++;Du[T]++;
        f[i+n].push_back(T);flow[i+n].push_back(x);FB[i+n].push_back(Du[T]-1);
        f[T].push_back(i+n);flow[T].push_back(0);FB[T].push_back(Du[i+n]-1);
    }
}
void work()
{
    int x;
    while(BFS())
        while(x=DFS(0,0x7fffffff))
            ANS-=x;
    BFS();
}
void print()
{
    for(int i=1;i<=n;i++)
        if(dis[i]>=0)
            printf("%d ",i);
    putchar('\n');
    for(int i=1;i<=m;i++)
        if(dis[i+n]>=0)
            printf("%d ",i);
    putchar('\n');
    printf("%d",ANS);
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值