BZOJ 1565: [NOI2009]植物大战僵尸

时间:2023-03-08 22:26:19

1565: [NOI2009]植物大战僵尸

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 2317  Solved: 1071
[Submit][Status][Discuss]

Description

BZOJ 1565: [NOI2009]植物大战僵尸

Input

BZOJ 1565: [NOI2009]植物大战僵尸

Output

仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。

Sample Input

3 2
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0

Sample Output

25

HINT

在样例中, 植物P1,1可以攻击位置(0,0), P2, 0可以攻击位置(2,1)。 
一个方案为,首先进攻P1,1, P0,1,此时可以攻击P0,0 。共得到能源收益为(-5)+20+10 = 25。注意, 位置(2,1)被植物P2,0保护,所以无法攻击第2行中的任何植物。 
【大致数据规模】
约20%的数据满足1 ≤ N, M ≤ 5;
约40%的数据满足1 ≤ N, M ≤ 10;
约100%的数据满足1 ≤ N ≤ 20,1 ≤ M ≤ 30,-10000 ≤ Score ≤ 10000 。

Source

分析:

看到这题就觉得亲切,这不是最大权闭合子图么...被保护的点向保护它的点连边,然后就是裸的最大权闭合子图...

但是发现过不了样例...TAT...因为有环...

所以我们要要删掉环的点,并且如果环上的点不能选,那么指向它的点也不能选...怎么办?拓扑排序?

但是拓扑排序只能判断有没有环,不能判断点是否在环上...比如下面这张图...

BZOJ 1565: [NOI2009]植物大战僵尸

但是很巧的是我们的图是所有指向环的点都不能选...所以反向所有边...拓扑排序无法访问到的点就是需要删掉的点...(WA了好久QAQ...感谢LMY...)

代码:

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
//by NeighThorn
#define inf 0x7fffffff
using namespace std; const int maxn=+,maxm=+; int n,m,S,T,cnt,ans,hd[maxn],fl[maxm],in[maxn],to[maxm],nxt[maxm],pos[maxn],val[maxn],vis[maxn],mp[maxn][maxn]; inline bool bfs(void){
memset(pos,-,sizeof(pos));
int head=,tail=,q[maxn];
q[]=S,pos[S]=;
while(head<=tail){
int top=q[head++];
for(int i=hd[top];i!=-;i=nxt[i])
if(pos[to[i]]==-&&fl[i])
pos[to[i]]=pos[top]+,q[++tail]=to[i];
}
return pos[T]!=-;
} inline int find(int v,int f){
if(v==T)
return f;
int res=,t;
for(int i=hd[v];i!=-&&f>res;i=nxt[i])
if(pos[to[i]]==pos[v]+&&fl[i])
t=find(to[i],min(fl[i],f-res)),fl[i]-=t,fl[i^]+=t,res+=t;
if(!res)
pos[v]=-;
return res;
} inline int dinic(void){
int res=,t;
while(bfs())
while(t=find(S,inf))
res+=t;
return res;
} inline void topo(void){
queue<int> q;
for(int i=;i<=n*m;i++)
if(!in[i])
q.push(i),vis[i]=;
while(!q.empty()){
int top=q.front();q.pop();
for(int i=;i<=n*m;i++)
if(mp[top][i]){
in[i]-=mp[top][i];
if(!in[i])
q.push(i),vis[i]=;
}
}
} inline void add(int s,int x,int y){
fl[cnt]=s;to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++;
fl[cnt]=;to[cnt]=x;nxt[cnt]=hd[y];hd[y]=cnt++;
} signed main(void){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
memset(in,,sizeof(in));
memset(hd,-,sizeof(hd));
scanf("%d%d",&n,&m);T=n*m+;
for(int i=,s;i<=n*m;i++){
scanf("%d%d",&val[i],&s);
for(int j=,x,y;j<=s;j++){
scanf("%d%d",&x,&y),x++,y++;
in[(x-)*m+y]++;mp[i][(x-)*m+y]++;
}
if(i%m)
in[i]++,mp[i+][i]++;
}
topo();
for(int i=;i<=n*m;i++)
if(vis[i]){
if(val[i]>)
ans+=val[i],add(val[i],S,i);
else
add(-val[i],i,T);
for(int j=;j<=n*m;j++)
if(vis[j]&&mp[i][j])
add(inf,j,i);
}
printf("%d\n",max(ans-dinic(),));
return ;
}//Cap ou pas cap. Cap.

By NeighThorn