正文:
有一个不可忽视的点,每个植物除了可以保护它攻击范围内的植物,还可以保护他后面的植物。
僵尸吃掉一个植物的前提条件是已经吃掉了保护它的所有植物。然后我们将保护这样一个行为建边(这个时候边是指向被保护的植物)。如果它构成了一个环,那么环上的植物以及被环上植物所保护的植物(直接与间接都算)都不能被僵尸所吃。我们将这个点mark掉。
这里可以先拓扑排序之后,剩下不能拓扑的就是我们要mark的植物。
故对于剩下的植物我们就是要求一个最大权闭合子图(因为吃掉一个植物的前提是吃掉所有保护它的植物,我们这个时候将边指向施加保护的植物),求最小割即可。
不会最大权闭合子图可以baidu一下。
DEBUG小提示:如果一开始的边就指向施加保护的植物的话就不可以在拓扑排序不能拓扑的就是环上的植物和保护环上植物的植物,但是保护环上植物的植物还是能被吃的,所以就错了(由于数据强度问题,这样写的分还是蛮高的)。
AC代码:
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
#define N 605
#define M 35
#define INF 1000000000
using namespace std;
void check_max(int &x,int y){if(x<y)x=y;}
void check_min(int &x,int y){if(x>y)x=y;}
struct E{
int to,nx,d;
}edge[N*N<<1];
int tot,head[N];
void Addedge(int a,int b,int d){
edge[++tot].to=b;
edge[tot].nx=head[a];
edge[tot].d=d;
head[a]=tot;
}
int n,m;
int val[N],ID[M][M];
vector<int>way[N];
int Deg[N];
bool mark[N];
void Topo(){
queue<int>Q;
for(int i=1;i<=n*m;i++)if(!Deg[i]){Q.push(i);mark[i]=true;}
while(!Q.empty()){
int now=Q.front();Q.pop();
for(int i=0;i<(int)way[now].size();i++){
int nxt=way[now][i];
Deg[nxt]--;
if(!Deg[nxt]){mark[nxt]=true;Q.push(nxt);}
}
}
}
int s,t;
int dep[N],cur[N];
bool bfs(){
queue<int>Q;Q.push(s);
memset(dep,63,sizeof(dep));
for(int i=1;i<=n*m+2;i++)cur[i]=head[i];
dep[s]=1;
while(!Q.empty()){
int now=Q.front();Q.pop();
for(int i=head[now];~i;i=edge[i].nx){
int nxt=edge[i].to;
if(!edge[i].d||dep[nxt]<INF)continue;
dep[nxt]=dep[now]+1;
Q.push(nxt);
}
}
return dep[t]<INF;
}
int dfs(int now,int lim){
if(now==t)return lim;
int res=0;
for(int i=cur[now];~i;i=edge[i].nx){
cur[now]=i;
int nxt=edge[i].to;
if(!edge[i].d)continue;
if(dep[nxt]==dep[now]+1){
int tmp=dfs(nxt,min(edge[i].d,lim));
res+=tmp;
lim-=tmp;
edge[i].d-=tmp;
edge[i^1].d+=tmp;
if(!lim)break;
}
}
return res;
}
int network_flow(){
int res=0;
while(bfs())res+=dfs(s,INF);
return res;
}
void Solve(){
Topo();//拓扑排序mark掉一定吃不掉的植物
s=n*m+1,t=n*m+2;
int sum=0;//正权值之和
for(int now=1;now<=n*m;now++){
if(!mark[now])continue;//一定吃不掉的植物不用参与建图
for(int i=0;i<(int)way[now].size();i++){
int nxt=way[now][i];
if(!mark[nxt])continue;
Addedge(nxt,now,INF);//注意这里边的方向是指向施加保护的植物
Addedge(now,nxt,0);
}
if(val[now]>=0){Addedge(s,now,val[now]);Addedge(now,s,0);sum+=val[now];}
else {Addedge(now,t,-val[now]);Addedge(t,now,0);}
}
int ans=sum-network_flow();//求最大权闭合子图
printf("%d\n",ans);
}
int main(){
tot=-1;memset(head,-1,sizeof(head));//Dinic的初始化
scanf("%d%d",&n,&m);
for(int i=1,id=0;i<=n;i++)for(int j=1;j<=m;j++)ID[i][j]=++id;//编号
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
int now=ID[i][j];
scanf("%d",&val[now]);
int sz;
scanf("%d",&sz);
while(sz--){
int x,y;
scanf("%d%d",&x,&y);
x++;y++;
Deg[ID[x][y]]++;
way[now].push_back(ID[x][y]);//首先将边指向被保护的植物
}
if(j!=1){way[now].push_back(ID[i][j-1]);Deg[ID[i][j-1]]++;}//在它后面的植物也可以被保护
}
Solve();
return 0;
}