您的位置:首页 > 理论基础 > 计算机网络

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

2017-05-25 10:21 465 查看
题面: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络流 最小割