题目

解析:

这是一道经典的最大权闭合子图转网络流的题。首先介绍一下什么是最大权闭合子图。

在一个图中,我们选取一些点构成集合,记为V,且集合中的出边(即集合中的点的向外连出的弧),所指向的终点(弧头)也在V中,则我们称V为闭合图。最大权闭合图即在所有闭合图中,集合中点的权值之和最大的V,我们称V为最大权闭合图。

下面通过一个例子来说明如何求最大权闭合子图网络流二十四题——太空飞行计划_最大流
上图是一个有向连通图,每个点带有一个权值。
此时,构建一个超级源点\(s\),一个超级汇点\(t\),所有的点按权值的正负连接到s和t上,转换成一个边权值有向图,如下图:网络流二十四题——太空飞行计划_最大流_02
\(s\)为起点,\(t\)为终点进行最小割,含有\(s\)的子图即为最大权闭合子图,最大权值为

\[\sum正权值-最大流 \]

详细证明参考最大权闭合子图 - [求最大点权的闭合子图]

回到这道题,实验和仪器之间存在依赖关系,这样的关系我们就可以用有向边来表示。表示选了一个实验就必须选择相应的仪器。再用上述方法建图即可。

code:

#include <bits/stdc++.h>
using namespace std;

const int Maxn=205;
const int Maxm=40000;
const int inf=1e9;
int n,m,size=-1,ans,tag,s,t,sum,s1,s2;
int c[Maxn],w[Maxn],dep[Maxn],f[Maxn][Maxn],first[Maxn],tmp[Maxn],outn[Maxn],outm[Maxn],vis[Maxn];
struct shu{int to,next,len;}edge[Maxm<<1];

inline int get_int()
{
	int x=0,f=1;char c;
	for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
	if(c=='-') f=-1,c=getchar();
	for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
	if(c!=' ') tag=1;
	return x*f;
}

inline void init()
{
	n=get_int(),m=get_int();
	for(int i=1;i<=n;i++)
	{
	  c[i]=get_int(),tag=0,sum+=c[i];
	  while(1)
	  {
	  	int x=get_int();
	  	f[i][x]=1;
	  	if(tag) break;
	  }
	}
	for(int i=1;i<=m;i++) w[i]=get_int();
}

inline void build(int x,int y,int z)
{
	edge[++size].next=first[x],first[x]=size,edge[size].to=y,edge[size].len=z;
}

inline void pre()
{
	s=0,t=n+m+1;
	for(int i=s;i<=t;i++) first[i]=-1;
	for(int i=1;i<=n;i++)
	{
	  build(s,i,c[i]),build(i,s,0);
	  for(int j=1;j<=m;j++)
	  {
	  	if(f[i][j]) build(i,j+n,inf),build(j+n,i,0);
	  }
	}
	for(int i=1;i<=m;i++) build(i+n,t,w[i]),build(t,i+n,0); 
}

inline bool bfs()
{
	queue<int>q;
	for(int i=s;i<=t;i++) dep[i]=0;
	dep[s]=1,q.push(s);
	while(q.size())
	{
	  int p=q.front();q.pop();
	  for(int u=first[p];~u;u=edge[u].next)
	  {
	  	int to=edge[u].to;
	  	if(dep[to] || !edge[u].len) continue;
	  	dep[to]=dep[p]+1,q.push(to);
	  	if(to==t) return 1;
	  }
	}
	return 0;
}

inline int dinic(int p,int flow)
{
	if(p==t) return flow;
	int sum=0;
	for(int &u=tmp[p];~u;u=edge[u].next)
	{
	  int to=edge[u].to;
	  if(dep[to]!=dep[p]+1 || !edge[u].len) continue;
	  int minn=dinic(to,min(flow-sum,edge[u].len));
	  sum+=minn,edge[u].len-=minn,edge[u^1].len+=minn;
	  if(sum==flow) break;
	}
	return sum;
}

inline void dfs(int p)
{
	vis[p]=1;
	for(int u=first[p];~u;u=edge[u].next)
	{
	  int to=edge[u].to;
	  if(vis[to] || !edge[u].len) continue;
	  dfs(to);
	}
}

inline void solve()
{
	while(bfs())
	{
	  for(int i=s;i<=t;i++) tmp[i]=first[i];
	  ans+=dinic(s,inf);
	}
	dfs(s);
	for(int i=1;i<=n;i++) if(vis[i]) outn[++s1]=i;
	for(int i=1;i<=m;i++) if(vis[i+n]) outm[++s2]=i;
	for(int i=1;i<=s1-1;i++) cout<<outn[i]<<" ";cout<<outn[s1]<<"\n";
	for(int i=1;i<=s2-1;i++) cout<<outm[i]<<" ";cout<<outm[s2]<<"\n";
	cout<<sum-ans<<"\n";	
}

int main()
{
	init();
	pre();
	solve();
	return 0;
}