[网络流24题]太空飞行计划问题(最小割)

题意

给定n个实验,m个机器,选一个实验得到ai价值,选一个机器有bi代价,每一个实验必须要某些机器参与才能进行,问获得的最大价值

思路

对于每一个实验,分为两种选择:

  1. 选取这个实验,那么必须选取其对应的所有机器

  2. 不选这个实验,那么其对应的机器只受到其他实验选择的影响

也就是说在实验和机器中选择一部分“放弃”,使得总价值最大,考虑最小割

建两排点分别表示实验和机器,用权值为ai的边连接源点与实验,权值为bi的边连接机器与汇点,实验与其相关的机器连权值为INF的边,那么ans=sigma(ai)-最小割

通过这样建图就可以发现,由于中间连边为INF不可能被切掉,所以被切掉的边只能是实验或者它对应的所有机器

由于原题还需要输出方案,只需要输出最后一次bfs(即没有増广路)可以到达的实验以及其对应的机器即可

Code:

#include<bits/stdc++.h>
#define N 105
using namespace std;
const int INF = 2000000000;
int n,m,s=101,t=102,sum;
bool flag;
int dep[N];
struct Edge
{
    int next,to,flow;
}edge[10005];int head[N],cur[N],cnt=1;
void add_edge(int from,int to,int flow)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    edge[cnt].flow=flow;
    head[from]=cnt;
}
void add(int from,int to,int flow)
{
    add_edge(from,to,flow);
    add_edge(to,from,0); 
}
int read()
{
    char ch=' ';int res=0;
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) res=res*10+ch-'0',ch=getchar();
    if(ch=='\n') flag=1;
    return res;
}
bool bfs()
{
    queue<int> q;
    memset(dep,0,sizeof(dep));
    for(int i=1;i<=104;++i) cur[i]=head[i];
    q.push(s); dep[s]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].flow<=0||dep[v]) continue;
            dep[v]=dep[u]+1;
            q.push(v);
            if(v==t) return true;
        }
    }
    return false;
}
int dfs(int rt,int rest)
{
    if(rt==t) return rest;
    int used=0;
    for(int i=cur[rt];i;i=edge[i].next)
    {
        cur[rt]=i;
        int v=edge[i].to;
        if(dep[v]==dep[rt]+1&&edge[i].flow>0)
        {
            int k=dfs(v,min(edge[i].flow,rest-used));
            if(!k) dep[v]=0;
            edge[i].flow-=k;
            edge[i^1].flow+=k;
            used+=k;
        }
    }
    return used;
}
int dinic()
{
    int ret=0,flow=0;
    while(bfs()) while((flow=dfs(s,INF))) ret+=flow;
    return ret;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
    {
        int earn=read();flag=0;
        sum+=earn;
        add(s,i,earn);
        while(!flag)
        {
            int ned=read();
            add(i,ned+50,INF);
        }
    }
    for(int i=1;i<=m;++i)
    {
        int cost=read();
        add(i+50,t,cost);
    }
    int maxx=dinic();
    for(int i=1;i<=50;++i) if(dep[i]) printf("%d ",i);
    printf("\n");
    for(int i=1;i<=50;++i) if(dep[i+50]) printf("%d ",i);
    printf("\n");
    printf("%d\n",sum-maxx);
    return 0;
}

转载于:https://www.cnblogs.com/Chtholly/p/11203688.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值