【网络流24题】太空飞行计划问题
标签:最小割
听说这玩意叫最大权闭合图?
Solution
观察原条件“选择实验 \(E_i\),必须已经配置仪器 \(I_{j \in R_i}\)”,是一对多的约束条件,比较难处理。
考虑如何转化成一对一的条件,发现可以将其转化为“不选择 \(I_{j \in R_i}\) 就不能选择 \(E_i\)”。
如果将选与不选看成两个集合,那么题目变成了一个经典二选一问题,可以用最小割处理。
考虑所有实验都选择,所有器材都不选择,此时利润为 \(Sum\)。此时情况是矛盾的,需要使用最小的代价使其合法。
设与源点 \(s\) 相连的集合 \(S\) 表示不选,与汇点 \(t\) 相连的集合 \(T\) 表示选。
那么连接源点 \(s\) 与 \(I_i\),将其容量设置为 \(c_i\),表示选择 \(I_i\) 需要花费 \(c_i\);
连接 \(E_i\) 与 汇点 \(t\),将其容量设置为 \(p_j\),表示不选择这个实验会损失 \(p_i\) 利润;
连接 \(I_{j \in R_i}\) 与 \(E_i\) ,将其容量设置为 \(inf\),表示不选 \(I_{j \in R_i}\) 就不能选择 \(E_i\)。
最后跑一边最大流,得到最小割 \(Flow\)。那么最终答案即为 \(Sum-Flow\)。
关于构造答案,从源点 \(s\) 出发沿着残留网络遍历到位于集合 \(S\) 的点,剩余的点皆为位于 \(T\) 的点。如此就可以知道有哪些实验与器材被选择。
Code
#include<bits/stdc++.h>
#define M 5005
#define N 205
using namespace std;
typedef long long ll;
typedef double db;
char IO;
int rd(){
int num=0;bool f=0;
while(IO=getchar(),IO<48||IO>57)if(IO=='-')f=1;
do num=(num<<1)+(num<<3)+(IO^48);
while(IO=getchar(),IO>=48&&IO<=57);
return f?-num:num;
}
int to[M],nxt[M],val[M],hd[N],cnte=1;
void Adde(int u,int v,int w){
to[++cnte]=v;val[cnte]=w;
nxt[cnte]=hd[u];hd[u]=cnte;
}
void Add(int u,int v,int w){
Adde(u,v,w);Adde(v,u,0);
}
int n,m,s,t,ans;
int dep[N],cur[N];
queue<int> Q;
int BFS(){
memset(dep,0,sizeof(dep));
memcpy(cur,hd,sizeof(cur));
dep[s]=1;Q.push(s);
int u,v;
while(!Q.empty()){
u=Q.front();Q.pop();
for(int i=hd[u];i;i=nxt[i]){
if(!dep[v=to[i]]&&val[i])
dep[v]=dep[u]+1,Q.push(v);
}
}
return dep[t];
}
int DFS(int u,int flow){
if(u==t)return flow;
int f,res=0;
for(int i=cur[u],v;i&&flow;i=nxt[i]){
cur[u]=i;v=to[i];
if(dep[v]!=dep[u]+1)continue;
if(val[i]&&(f=DFS(v,min(flow,val[i])))>0){
val[i]-=f;
val[i^1]+=f;
res+=f;flow-=f;
}
}
return res;
}
bool Set[N];
void Find(int s){
Set[s]=1;
for(int i=hd[s],y;i;i=nxt[i]){
if(!val[i]||Set[y=to[i]])continue;
Find(y);
}
}
int A[N],B[N];
int main(){
n=rd(),m=rd();
s=0,t=n+m+1;
for(int i=m+1,x;i<=m+n;++i){
Add(i,t,x=rd());ans+=x;
while(IO!='\r'&&IO!='\n')
Add(rd(),i,1e9);
}
for(int i=1;i<=m;++i)
Add(s,i,rd());
while(BFS())
ans-=DFS(s,1e9);
Find(s);
for(int i=m+1;i<=m+n;++i)
if(!Set[i])printf("%d ",i-m);
puts("");
for(int i=1;i<=m;++i)
if(!Set[i])printf("%d ",i);
puts("");
cout<<ans;
return 0;
}

网络流/最小割
浙公网安备 33010602011771号