【网络流】太空飞行计划

最大权闭合图问题,可以转化成最小割问题,进而用最大流解决。
【建模方法】

把每个实验看作二分图X集合中的顶点,每个设备看作二分图Y集合中的顶点,增加源S和汇T。
1、从S向每个Xi连接一条容量为该点收入的有向边。
2、从Yi向T连接一条容量为该点支出的有向边。
3、如果一个实验i需要设备j,连接一条从Xi到Yj容量为无穷大的有向边。

统计出所有实验的收入和Total,求网络最大流Maxflow,最大收益就是Total-Maxflow。对应的解就是最小割划分出的S集合中的点,也就是最后一次增广找到阻塞流时能从S访问到的顶点。


#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
#define maxn  200
#define INF 0x3f3f3f3f

struct EDGE{
       int to,cap,op,next;
       }e[maxn*maxn];

int last[maxn],S,T,nodes,m,n,tot,dep[maxn],gap[maxn],money=0,cost=0,v[maxn];

void add(int x,int y,int w){
     e[++tot].to=y;
     e[tot].cap=w;
     e[tot].op=tot+1;
     e[tot].next=last[x];
     last[x]=tot;
     e[++tot].to=x;
     e[tot].cap=0;
     e[tot].op=tot-1;
     e[tot].next=last[y];
     last[y]=tot;
     }

int SAP(int now,int delta){
    if (now==T) return delta;
    int mindis=nodes,sum=0;
    for (int j=last[now];j;j=e[j].next){
        if (e[j].cap>0 && dep[e[j].to]+1==dep[now]){
            int save=SAP(e[j].to,min(delta-sum,e[j].cap));
            sum+=save;
            e[j].cap-=save;
            e[e[j].op].cap+=save;
            if (sum==delta || dep[S]>=nodes) return sum;
            }
        if (e[j].cap>0) mindis=min(mindis,dep[e[j].to]);
    }
    if (sum==0){
        if (!--gap[dep[now]]) dep[S]=nodes;
          else ++gap[dep[now]=mindis+1];
        }
    return sum;
}


void dfs(int s){
     v[s]=true;
     for (int j=last[s];j;j=e[j].next)
         if (e[j].cap>0 && !v[e[j].to]) dfs(e[j].to);
}

int main(){
    freopen("shuttle.in","r",stdin);
    freopen("shuttle.out","w",stdout);
    scanf("%d%d",&m,&n);
    S=0; T=n+m+1;
    nodes=T+1;
    memset(dep,0,sizeof(dep));
    gap[0]=nodes;
    int x,a;
    char b;
    for (int i=1;i<=m;i++){
        scanf("%d",&x);
        add(S,i,x);
        money+=x;
        while (scanf("%d%c",&a,&b)&& a){ 
           add(i,a+m,INF);
           if (b=='\n') break;
        }
    }
    for (int i=1;i<=n;i++) {
        scanf("%d",&x);
        add(i+m,T,x);
        }
    while (dep[S]<nodes) cost+=SAP(S,INF);
    memset(v,false,sizeof(v));
    dfs(S);
    for (int i=1;i<=m;i++) if (v[i]) printf("%d ",i);
    printf("\n");
    for (int i=1;i<=n;i++) if (v[i+m]) printf("%d ",i);
    printf("\n");
    printf("%d\n",money-cost);
}
    
            


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值