「NOI2009」植物大战僵尸 网络流最大权闭合子图

「NOI2009」植物大战僵尸

正文:

有一个不可忽视的点,每个植物除了可以保护它攻击范围内的植物,还可以保护他后面的植物。

僵尸吃掉一个植物的前提条件是已经吃掉了保护它的所有植物。然后我们将保护这样一个行为建边(这个时候边是指向被保护的植物)。如果它构成了一个环,那么环上的植物以及被环上植物所保护的植物(直接与间接都算)都不能被僵尸所吃。我们将这个点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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值