题目
解析:
这是一道经典的最大权闭合子图转网络流的题。首先介绍一下什么是最大权闭合子图。
在一个图中,我们选取一些点构成集合,记为V,且集合中的出边(即集合中的点的向外连出的弧),所指向的终点(弧头)也在V中,则我们称V为闭合图。最大权闭合图即在所有闭合图中,集合中点的权值之和最大的V,我们称V为最大权闭合图。
下面通过一个例子来说明如何求最大权闭合子图
上图是一个有向连通图,每个点带有一个权值。
此时,构建一个超级源点\(s\),一个超级汇点\(t\),所有的点按权值的正负连接到s和t上,转换成一个边权值有向图,如下图:
以\(s\)为起点,\(t\)为终点进行最小割,含有\(s\)的子图即为最大权闭合子图,最大权值为
详细证明参考最大权闭合子图 - [求最大点权的闭合子图]
回到这道题,实验和仪器之间存在依赖关系,这样的关系我们就可以用有向边来表示。表示选了一个实验就必须选择相应的仪器。再用上述方法建图即可。
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;
}