1. 程式人生 > 實用技巧 >題解 [NOI2009] 植物大戰殭屍

題解 [NOI2009] 植物大戰殭屍

  講網路流的時候沒有聽懂最大權閉合子圖,然後講題的時候學長問我們這是什麼模型。

  我就瞎口胡了最大權閉合子圖,然後就中了。

  總之,依題可知,要吃一個植物,必須吃它右側的植物,並且吃掉保護它的植物(全都是玉米加農炮嗷)。所以在兩個點之間連一條有向邊,連完之後發現要獲取一個點的分數必須獲取所有它連線的點的分數。

  所以就是最大權閉合子圖辣。不過由於一個植物可能會保護它自己,或者保護(保護它的植物),所以判環,環與環到達的點統統不拿來建圖。建個反圖跑拓撲排序就好了。

  程式碼:

#include <bits/stdc++.h>
#define maxn 720005
#define
inf 0x3f3f3f using namespace std; long long n,m,tot=1,ans=0; queue <int > q; int head[maxn],nex[maxn],to[maxn],edge[maxn],cur[maxn]; int mapp[300][300],vis[maxn],lev[maxn]; int attack[605][605],start,endn,cango[maxn]; int in[maxn]; void add(int x,int y,int z){ to[++tot]=y;edge[tot]=z;nex[tot]=head[x];head[x]=tot; }
int turn(int x,int y){ if (x==0) return 0; return (x-1)*m+y; } int turn1(int x,int y){ return x*m+y+1; }//這兩個turn是轉二維座標為一維用的,看自己喜歡怎麼做來咯 void t_sort(){//拓撲排序 for (int i=2;i<=tot;i++){ in[to[i]]++; } for (int i=1;i<=n*m;i++) { if (!in[i]) { q.push(i); cango[i]
=1; } } while (!q.empty()){ int x=q.front(); q.pop(); cango[x]=1; for (int i=head[x];i;i=nex[i]){ in[to[i]]--; if (!in[to[i]]) q.push(to[i]); } } } void build(){//建圖 tot=1; memset(head,0,sizeof(head)); memset(nex,0,sizeof(nex)); memset(to,0,sizeof(to)); memset(edge,0,sizeof(edge)); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++){ int x=turn(i,j); if (!cango[x]) continue; if (j<m) { add(x,x+1,inf); add(x+1,x,0); } for (int l=1;l<=attack[x][0];l++){ int y=attack[x][l]; if (!cango[y]) continue; add(y,x,inf);add(x,y,0); } } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++){ int x=turn(i,j); if (!cango[x]) continue; if (mapp[i][j]>0) add(start,x,mapp[i][j]),add(x,start,0),ans+=mapp[i][j]; if (mapp[i][j]<0) add(x,endn,-mapp[i][j]),add(endn,x,0); } } void f_build(){//建反圖,反圖只用建原圖中的邊就好了 for (int i=1;i<=n;i++) for (int j=1;j<=m;j++){ int x=turn(i,j); if (j<m) { add(x+1,x,inf); } for (int l=1;l<=attack[x][0];l++){ int y=attack[x][l]; add(x,y,inf); } } } int bfs(){//bfs定層數,找有無增廣路 memset(vis,0,sizeof(vis)); memset(lev,0,sizeof(lev)); q.push(start);vis[start]=1; while (!q.empty()) { int x=q.front(); cur[x]=head[x]; q.pop(); for (int i=head[x];i;i=nex[i]) { int y=to[i]; if (edge[i]>0 && !vis[y]) { q.push(y);vis[y]=1;lev[y]=lev[x]+1; } } } if (!lev[endn]) return 0; else return 1; } int dfs(int x,int rest){//dfs找增廣路(dinic) if (x==endn) return rest; int sum=0; for (int i=cur[x];i;i=nex[i]){ int y=to[i]; cur[x]=i; if (edge[i]>0 && lev[y]==lev[x]+1){ int d=dfs(y,min(edge[i],rest-sum)); sum+=d;edge[i]-=d;edge[i^1]+=d; } if (sum==rest) break; } if (!sum) lev[x]=0; return sum; } void dinic(){ while (bfs()){ ans-=dfs(start,inf); } } int main(){ scanf("%lld%lld",&n,&m); start=n*m+1;endn=n*m+2; for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++){ int p,w; scanf("%d%d",&p,&w); mapp[i][j]=p; int x=turn(i,j); attack[x][0]=w; for (int l=1;l<=w;l++) { int z,z1; scanf("%d%d",&z,&z1); attack[x][l]=turn1(z,z1); } } } f_build(); t_sort(); build(); dinic(); printf("%lld\n",ans); return 0; }