题目:洛谷P2805、BZOJ1565。
题目大意:
在n×m的网格上,每个格子有植物,吃了每个植物能获得一些价值(可能为负),每个植物可能会保护另一些植物,要吃被保护的植物必须先消灭该植物。你可以在最右边放僵尸。问最大价值。
解题思路:
PVZ真好玩
首先,右边的植物也是保护左边的植物的。
然后,就是最大权闭合子图模型。最大流即可。
从S向价值为正的植物连容量为价值的边;从价值为负的植物向T连容量为价值的相反数的边。
从被保护的植物向保护的植物连容量inf的边。
答案即为价值为正的植物的价值总和-最大流。
然鹅发现并过不去。因为有环!植物和植物可能存在着循环保护,然后这些植物就无敌了(无敌植物所保护的其他植物也无敌)。
故我们首先要用Tarjan找强连通分量,把环找出来,然后删掉这些无敌的点(包括其保护的点等)。
接着再建图跑网络流即可。
C++ Code:
#include<bits/stdc++.h>
const int S=0,T=6665,inf=0x3fffffff;
inline int readint(){
int c=getchar(),d=0,f=0;
for(;!isdigit(c);c=getchar())f=c=='-';
for(;isdigit(c);c=getchar())
d=(d<<3)+(d<<1)+(c^'0');
return f?-d:d;
}
int n,m,sco[666],low[666],dfn[666],idx=0,vis[666],head[6666],iter[6666],level[6666],cnt=1;
bool ok[666];
inline int num(int a,int b){return(a-1)*m+b;}
inline int min(int a,int b){return a<b?a:b;}
std::vector<int>v[666];
std::stack<int>s;
void tarjan(int now){
low[now]=dfn[now]=++idx;
s.push(now);vis[now]=1;
for(int i=v[now].size()-1;~i;--i)
if(!dfn[v[now][i]]){
tarjan(v[now][i]);
low[now]=min(low[now],low[v[now][i]]);
}else
if(vis[v[now][i]])low[now]=min(low[now],dfn[v[now][i]]);
if(low[now]==dfn[now]){
int sz=1;
for(;s.top()!=now;++sz){
ok[s.top()]=false;
vis[s.top()]=0;
s.pop();
}
vis[now]=0;
s.pop();
if(sz>1)ok[now]=false;
}
}
struct edge{
int to,nxt,cap;
}e[2333333];
inline void addedge(int from,int to,int flow){
e[++cnt]=(edge){to,head[from],flow};
head[from]=cnt;
e[++cnt]=(edge){from,head[to],0};
head[to]=cnt;
}
std::queue<int>q;
void bfs(){
level[S]=1;
for(q.push(S);!q.empty();){
int u=q.front();
q.pop();
for(int i=head[u];~i;i=e[i].nxt)
if(e[i].cap&&!~level[e[i].to]){
level[e[i].to]=level[u]+1;
q.push(e[i].to);
}
}
}
int dfs(int u,int f){
if(!f||u==T)return f;
for(int& i=iter[u];~i;i=e[i].nxt)
if(e[i].cap&&level[e[i].to]>level[u]){
int d=dfs(e[i].to,min(f,e[i].cap));
if(d){
e[i].cap-=d;
e[i^1].cap+=d;
return d;
}else level[e[i].to]=-1;
}
return 0;
}
int dinic(){
for(int flow=0,f;;){
memset(level,-1,sizeof iter);
if(bfs(),!~level[T])return flow;
memcpy(iter,head,sizeof iter);
while(f=dfs(S,inf))flow+=f;
}
}
void color(int now){
for(int i=v[now].size()-1;~i;--i)
if(ok[v[now][i]]){
ok[v[now][i]]=false;
color(v[now][i]);
}
}
int main(){
n=readint(),m=readint();
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
int id=num(i,j);
sco[id]=readint();
for(int cc=readint();cc--;){
int x=readint()+1,y=readint()+1;
v[id].push_back(num(x,y));
}
if(j>1)v[id].push_back(id-1);
}
memset(dfn,0,sizeof dfn);
memset(vis,0,sizeof vis);
memset(head,-1,sizeof head);
memset(ok,1,sizeof ok);
for(int i=1;i<=n*m;++i)
if(!dfn[i])tarjan(i);
int sm=0;
for(int i=1;i<=n*m;++i)
if(!ok[i])color(i);
for(int i=1;i<=n*m;++i)
if(ok[i]){
if(sco[i]>0)addedge(S,i,sco[i]);else
addedge(i,T,-sco[i]);
sm+=(sco[i]>0)*sco[i];
for(int j=v[i].size()-1;~j;--j)
if(ok[v[i][j]])addedge(v[i][j],i,inf);
}
printf("%d\n",sm-dinic());
return 0;
}