题意:N*M草地,每个植物有权值,可正可负。每个植物被同一行右边植物保护,每个植物还能额外保护一些其他的植物。求吃掉一些植物的最大收益。
n<=20,m<=30。
显然是最大权闭合子图模型,用最小割解决。
由于图里有环,所以不能直接跑网络流。(如果一个环中一个点连源点,一个点连汇点,最小割只会割掉其中一条,但实际上,所有环上的点都不能选。)
但是tarjan缩点是不行的,因为如果一条路径上出现不能选的点,那么这个点之后的点也都不能选,这就不好维护了。
考虑什么样的点不应存在于网络流的建图中。首先,环上的点不能选;其次,如果一个点指向不能选的点,那么这个点也不能选。将边反向,变为不能选的点指向的点也不能选,所以能选的点就是拓扑排序能走到的点。
之后正常建图跑最大流就可以了。
#include<cstdio>
#include<queue>
#include<cstring>
#define gm 800
using namespace std;
const int s=0,t=601,inf=0x7fffffff;
struct e
{
int t,flow;
bool is_r;
e *n,*r;
e(int t,e *n,int flow,bool is_r=0):t(t),n(n),flow(flow),is_r(is_r){}
}*f[gm];
int rd[gm],w[gm];
inline void link(int x,int y,int flow)
{
f[x]=new e(y,f[x],flow);
f[y]=new e(x,f[y],0,true);
f[x]->r=f[y],f[y]->r=f[x];
++rd[x];
}
bool ok[gm];
int n,m,ans=0;
inline void pre_choose()
{
queue<int> q;
for(int i=1;i<=n;++i) if(!rd[i]) q.push(i);
while(!q.empty())
{
int now=q.front();q.pop();
for(e *i=f[now];i;i=i->n)
if(i->is_r)
{
--rd[i->t];
if(!rd[i->t]) q.push(i->t);
}
ok[now]=1;
if(w[now]>0) ans+=w[now],link(s,now,w[now]);
else link(now,t,-w[now]);
}
}
inline int min(int a,int b){return a<b?a:b;}
int d[gm];
inline bool bfs()
{
queue<int> q;
memset(d,-1,sizeof d);
d[s]=0;
q.push(s);
while(!q.empty())
{
int now=q.front();q.pop();
if(now==t) return 1;
for(e *i=f[now];i;i=i->n)
if(ok[i->t]&&i->flow&&d[i->t]==-1)
d[i->t]=d[now]+1,q.push(i->t);
}
return 0;
}
int send(int now=s,int flow=inf)
{
if(now==t) return flow;
int last=flow;
for(e *i=f[now];i&&last;i=i->n)
if(d[i->t]==d[now]+1)
{
int kre=send(i->t,min(last,i->flow));
i->flow-=kre;
i->r->flow+=kre;
last-=kre;
}
if(last) d[now]=-1;
return flow-last;
}
int main()
{
scanf("%d%d",&n,&m);
ok[t]=1;
n*=m;
for(int i=1,k;i<=n;++i)
{
scanf("%d%d",w+i,&k);
if(i%m) link(i,i+1,inf);
while(k--)
{
int x,y;
scanf("%d%d",&x,&y);
link(x*m+y+1,i,inf);
}
}
pre_choose();
while(bfs())
ans-=send();
printf("%d\n",ans);
}