题解 [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; }