最大流——Luogu2762 [网络流24题]太空飞行计划问题

题面:Luogu2762
网络流24题之二
最大权闭合子图 ,首先建图
我们先从源点向每个实验连上权值为实验收益的边,再从每个器材向汇点权值为配置费用的边
然后每对配对方案连一条权值为INF的边
跑最小割即可,答案就是实验收益总和-最大流
具体证明不证了
两个比较容易炸的地方:

  • 读入:挺恶心的因为并没有告诉你到底配对的方案数有多少,请不要使用类似读入优化的读入方式(我试过好像这对于换行符不敏感。。。),推荐字符串getline读入后处理
  • 输出方案:首先找器材,我们可以试着把器材到汇点的边删去再跑最小割(放心,这题n<=50 m<=50 是T不掉的)然后对比答案如果后者答案少的就是权值那么说明这条边满流,器材可用,继而实验也可以推出来了
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<string>
#include<ctime>
#include<queue>
#include<climits>
using namespace std;
string ctrmmp;//请忽视这个变量名……读入专用
bool an[10001];
int dist[10001],cur[10001],n,m,s,t,sum=0,v[100001];
int nedge=-1,p[100001],c[100001],C[100001],nex[100001],head[100001];
inline void addedge(int x,int y,int z){
    p[++nedge]=y;C[nedge]=c[nedge]=z;
    nex[nedge]=head[x];head[x]=nedge;
}
inline bool bfs(int s,int t){
    queue<int>q;q.push(s);
    memset(dist,-1,sizeof dist);dist[s]=1;
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int k=head[now];k>-1;k=nex[k])if(c[k]&&dist[p[k]]==-1){
            dist[p[k]]=dist[now]+1;q.push(p[k]);
        }
    }
    return dist[t]>-1?1:0;
}
inline int dfs(int x,int low){
    if(x==t)return low;
    int a,used=0;
    for(int k=cur[x];k>-1;k=nex[k])if(c[k]&&dist[p[k]]==dist[x]+1){
        a=dfs(p[k],min(c[k],low-used));
        if(a)c[k]-=a,c[k^1]+=a,used+=a;
        if(c[k])cur[x]=k;
        if(used==low)break;
    }
    if(!used)dist[x]=-1;
    return used;
}
inline int dinic(){
    int flow=0;
    while(bfs(s,t)){
        for(int i=s;i<=t;i++)cur[i]=head[i];
        flow+=dfs(s,1e9);
    }
    return flow;
}
int main()
{
    memset(p,-1,sizeof p);memset(nex,-1,sizeof nex);
    memset(c,-1,sizeof c);memset(head,-1,sizeof head);
    scanf("%d%d",&n,&m);
    s=0;t=n+m+1;
    for(int i=1;i<=n;i++){
        int v;scanf("%d",&v);addedge(s,i,v);addedge(i,s,0);sum+=v;
        getline(cin,ctrmmp);ctrmmp+=' ';int l=ctrmmp.size();
        int k=0;
        for(int j=0;j<l;j++){
            if(ctrmmp[j]>='0'&&ctrmmp[j]<='9')k=k*10+ctrmmp[j]-'0';
            else if(k)addedge(i,n+k,1e9),addedge(n+k,i,0),k=0;
        }//读入
    }
    for(int i=1;i<=m;i++){
        int x;scanf("%d",&x);
        addedge(n+i,t,x);addedge(t,n+i,0);
    }
    int ans=dinic();
    for(int k=head[t];k>-1;k=nex[k]){
        memcpy(c,C,sizeof(C));
        int rp=c[k^1];c[k^1]=0;
        if(ans-dinic()==rp)an[p[k]-n]=1;
    }//记录器材是否可行
    for(int i=1;i<=n;i++){
        bool flag=1;
        for(int k=head[i];k>-1;k=nex[k])if(p[k]&&!an[p[k]-n]){flag=0;break;}
        if(flag)printf("%d ",i);
    }//记录实验是否可行
    puts("");for(int i=1;i<=m;i++)if(an[i])printf("%d ",i);
    printf("\n%d",sum-ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值