BZOJ 1565 植物大战僵尸 最大权闭合子图+网络流
题意:
植物大战僵尸,一个n*m的格子,每 个格子里有一个植物,每个植物有两个属性:
(1)价值;
(2)保护集合,也就是这个植物可以保护矩阵中的某些格子。
现在你是僵尸,你每次只能从(i,m) 格子进入,从右向左进攻。若一个格子是被保护的那么你是不能进入的。每进入一个格子则吃掉该格子的植物并得到其价值(价值有可能是负的),可以中途返回。问可以得到的最大价值是多少?
分析:
这是一道比较真实的题目。(真打游戏的时候应该也是这种景象吧)
首先我们复习一下最大权闭合子图的特质。
有一个有向图,每一个点都有一个权值(可以为正或负或0),选择一个权值和最大的子图,使得每个点的后继都在子图里面,这个子图就叫最大权闭合子图。
上面这句话我们要提炼出一个极其重要的信息:如果我们选择一个点,那就必须选择它的所有后继。
凡是题目中隐含着这样的条件的,我们都可以往最大权闭合子图方向去想一想。
像这道题,每个植物可以保护一些其他的植物。那就意味着,如果我们想要选择一个植物,我们必须首先把所有保护它的植物都选掉。
这样,我们就可以建图,对于一个点x,假如有点y可以保护点x,那么我们就连一条x—>y的边,注意,边的方向和保护的方向是相反的。
(这里有好多题解都不是这么说的,或许另有高论?)
还隐含着一个条件:右边的始终植物保护着左边的植物(对吧?植物大战僵尸里是这样的吧,所以诞生了高坚果)
我们建图,跑最大权闭合子图就好了吗?
并不是……
因为环是无敌的……???!!!
如果在保护关系中出现了环,那么你选任何一个,都是被保护的。
所以我们拓扑,把环的影响取消掉,再跑最大权闭合子图的恶意……
从源点s向每个正权点连一条容量为权值的边,每个负权点向汇点t连一条容量为权值的绝对值的边,有向图原来的边容量全部为无限大答案为正权值之和-最小割
代码:
1 #include<bits/stdc++.h> 2 #define ms(a,x) memset(a,x,sizeof(a)) 3 using namespace std;int tot=0,n,m,sm=0; 4 const int N=2005,inf=0x3f3f3f3f; 5 struct node{int y,z,nxt;}e[N*400]; 6 int h[N],c=1,q[N],in[N],S,T,d[N],a[N],ans=0; 7 void add(int x,int y,int z){in[x]++; 8 e[++c]=(node){y,z,h[x]};h[x]=c; 9 e[++c]=(node){x,0,h[y]};h[y]=c; 10 } bool bfs(){ 11 for(int i=S;i<=T;i++) 12 if(d[i]!=-2) d[i]=-1; 13 int f=1,t=0;d[S]=1;q[++t]=S; 14 while(f<=t){ 15 int x=q[f++]; 16 for(int i=h[x],y;i;i=e[i].nxt) 17 if(d[y=e[i].y]==-1&&e[i].z) 18 d[y]=d[x]+1,q[++t]=y; 19 } return (d[T]>0); 20 } int dfs(int x,int f){ 21 if(x==T) return f;int w,tmp=0; 22 for(int i=h[x],y;i;i=e[i].nxt) 23 if(d[y=e[i].y]==d[x]+1&&e[i].z){ 24 w=dfs(y,min(e[i].z,f-tmp)); 25 if(!w) d[y]=-1;e[i].z-=w; 26 e[i^1].z+=w;tmp+=w; 27 if(tmp==f) return f; 28 } return tmp; 29 } void solve(){ 30 while(bfs()) tot+=dfs(S,inf); 31 } int main(){ 32 scanf("%d%d",&n,&m);S=0;T=n*m+1; 33 for(int i=1,tmp;i<=n*m;i++){ 34 scanf("%d",&a[i]); 35 a[i]>0?add(S,i,a[i]):add(i,T,-a[i]); 36 scanf("%d",&tmp);while(tmp--){ 37 int x,y;scanf("%d%d",&x,&y); 38 add(x*m+y+1,i,inf); 39 } if(i%m) add(i,i+1,inf); 40 } int f=1,t=0;//图是反着建的,拓扑要倒过来 41 for(int i=S;i<=T;d[i]=-2,i++) 42 if(!in[i]) q[++t]=i; 43 while(f<=t){ 44 int x=q[f++];d[x]=0; 45 if(a[x]>0) sm+=a[x]; 46 for(int i=h[x];i;i=e[i].nxt) 47 if(i&1)if(!--in[e[i].y]) q[++t]=e[i].y; 48 } solve(); 49 printf("%d\n",sm-tot);return 0; 50 }