最大权闭合图问题,可以转化成最小割问题,进而用最大流解决。
【建模方法】
把每个实验看作二分图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);
}