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

链接

https://www.luogu.org/problemnew/show/P2762

大意

m 个实验,每个实验只可以进行一次,但会获得相应的奖金,有 n 个仪器,每个实验都需要一定的仪器,每个仪器可以运用于多个实验,但需要一定的价值,问奖金与代价的差的最大值是多少?

思路

这道题的读入真的坑死人!最后还是改成了读入优化才A的。。。
666
好了现在步入正轨

这道题无非是让我们权衡奖金与代价,这两者是有我没他的,怎么去处理呢,我们先建立一张图,所有的实验与原电相连,容量为其奖金,所有的器材与汇点相连,容量为其价格,中间实验与器材相连,容量为无穷大。
这个时候跑最小割,其必定会割掉连接实验或容器的边,因为中间的边的代价为无穷大,一定不会被割掉。
这个时候跑最大流相当于选择部分的实验和部分的仪器,剩下的实验和仪器就会被割掉,此时再用实验的总价值减去可能得到的最大值,即为其所要求的答案
如下图
流程图

代码

#include<cstring>
#include<cctype>
#include<cstdio>
#include<queue>
#define N 100001
using namespace std;int m,n,f,s,t,ci,pi,sum;char c;
bool flag;
struct node{int next,to,w;}e[N<<1];
int l[N],tot,d[N];
bool vis[N];
int read()
{
    f=0;
    while (c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
    while (c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
    if (c=='\n') flag=1;
    return f;
}
void add(int u,int v,int w)
{
    e[tot]={l[u],v,w};l[u]=tot++;
    e[tot]={l[v],u,0};l[v]=tot++;
    return;
}
bool bfs()
{
    memset(d,-1,sizeof(d));
    queue<int>q;d[s]=0;q.push(s);
    while(q.size())
    {
        int x=q.front();q.pop();
        for(int i=l[x];~i;i=e[i].next)
        {
            int y=e[i].to;
            if(e[i].w&&d[y]==-1)
            {
                d[y]=d[x]+1;
                q.push(y);
                if(y==t) return true;
            }
        }
    }
    return false;
}
int dfs(int x,int flow)
{
    if(x==t||!flow) return flow;
    int rest=0,k=0;
    for(int i=l[x];~i;i=e[i].next)
    {
        int y=e[i].to;
        if(d[x]+1==d[y]&&e[i].w)
        {
            f=dfs(y,min(flow-rest,e[i].w));
            if(!f) d[y]=-1;
            e[i].w-=f;rest+=f;e[i^1].w+=f;
        }
    }
    if(!rest) d[x]=-1;
    return rest;
}
void dinic()
{
    while(bfs()) sum-=dfs(s,50234567);
}
int main()
{
    memset(l,-1,sizeof(l));
    m=read();n=read();s=0;t=151;
    for(int i=1;i<=m;i++)
    {
        ci=read();
        add(s,i,ci);sum+=ci;flag=0;
        while(!flag) pi=read(),add(i,pi+50,1e9);
    }
    for(int i=1;i<=n;i++) pi=read(),add(i+50,t,pi);
    dinic();
    for(int i=1;i<=m;i++) if(d[i]!=-1) printf("%d ",i);putchar(10);
    for(int i=1;i<=n;i++) if(d[i+50]!=-1) printf("%d ",i);putchar(10);
    printf("%d",sum);
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/80782901