测试地址:植物大战僵尸
做法:上次我们提到了最大权闭合子图的模型(见这里),这一个题目也容易看出是最大权闭合子图的模型,当一个植物被另一个植物保护的时候,隐含的关系就是:如果要选择攻击被保护植物,就必须也攻击保护植物,因此从被保护植物向保护植物连边,然后按照方法来建图求解。
然而这还不够,在纯粹的求解最大权闭合子图的过程中,环要么就不取,要么就全部取走,而这一题的条件限定如果存在环一定不能取,进而指向环的所有点也不能取,那么怎么把这些点剔除掉呢?答案就是拓补排序。对原图的反图进行拓补排序,最后没有入过队的点就是不能取的点了,然后再跑最大流即可。
以下是本人代码(注意!本人的代码会TLE,只能拿到90分,有待学习):
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define inf 1000000000
using namespace std;
int n,m,first[1010]={0},tot=1;
int level[1010],sum=0,val[1010],in[1010]={0};
bool vis[1010]={0};
struct edge {int v,f,next;} e[1000010];
void insert(int a,int b,int f)
{
e[++tot].v=b,e[tot].f=f,e[tot].next=first[a],first[a]=tot;
e[++tot].v=a,e[tot].f=0,e[tot].next=first[b],first[b]=tot;
}
void clear()
{
queue<int> q;
for(int i=1;i<=n*m;i++)
if (!in[i]) q.push(i);
while(!q.empty())
{
int v=q.front();q.pop();
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=n*m+1&&e[i].v!=0&&!e[i].f)
{
in[e[i].v]--;
if (!in[e[i].v]) q.push(e[i].v);
}
vis[v]=1;
}
for(int i=1;i<=n*m;i++)
if (vis[i]&&val[i]>0) sum+=val[i];
}
bool makelevel()
{
queue<int> q;
memset(level,0,sizeof(level));
level[0]=1;
q.push(0);
while(!q.empty())
{
int v=q.front();q.pop();
for(int i=first[v];i;i=e[i].next)
if (!level[e[i].v]&&e[i].f&&vis[e[i].v])
{
level[e[i].v]=level[v]+1;
q.push(e[i].v);
}
}
return level[n*m+1];
}
int dfs(int v,int maxf)
{
int ret=0,f;
if (v==n*m+1) return maxf;
for(int i=first[v];i;i=e[i].next)
if (level[e[i].v]==level[v]+1&&e[i].f&&vis[e[i].v])
{
f=dfs(e[i].v,min(maxf-ret,e[i].f));
e[i].f-=f;
e[i^1].f+=f;
ret+=f;
if (ret==maxf) return ret;
}
return ret;
}
int dinic()
{
int ans=0;
while(makelevel())
{
ans+=dfs(0,inf);
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,b;i<=n*m;i++)
{
scanf("%d%d",&val[i],&b);
while(b--)
{
int x,y;
scanf("%d%d",&x,&y);
insert(x*m+y+1,i,inf);
in[x*m+y+1]++;
}
}
for(int i=0;i<n;i++)
for(int j=0;j<m-1;j++)
{
insert(i*m+j+1,i*m+j+2,inf);
in[i*m+j+1]++;
}
clear();
for(int i=1;i<=n*m;i++)
{
if (val[i]>0) insert(0,i,val[i]);
if (val[i]<0) insert(i,n*m+1,-val[i]);
}
vis[n*m+1]=1;
printf("%d",sum-dinic());
return 0;
}