题目:洛谷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 ",sm-dinic()); return 0; }