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


题目


1565: [NOI2009]植物大战僵尸

Time Limit: 10 Sec   Memory Limit: 64 MB

Description

Input

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 。


题解


这道题是最大权闭合子图,求一个最后没有出度的子图。用网络流来做,将所有点之间的边流量为INF,点权为正的连原点,流量为点权,点权为负的连汇点,流量为绝对值。答案为所有正点权和-最大流。但是这道题目还有环保护的问题,一旦环保护了,那么都不可以取,跑最大流会坑!我们要用拓扑排序来处理,最后不断的去掉入度为0的点,得到新的入度为0的点,一些点入度总是不能为0说明在环内,要去掉。这样之后跑最大流就好!【我做的时候拓扑排序思路不是太清晰QAQ


代码


/*Author:WNJXYK*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
using namespace std;

#define LL long long
#define Inf 2147483647
#define InfL 10000000000LL

inline void swap(int &x,int &y){int tmp=x;x=y;y=tmp;}
inline void swap(LL &x,LL &y){LL tmp=x;x=y;y=tmp;}
inline int remin(int a,int b){if (a<b) return a;return b;}
inline int remax(int a,int b){if (a>b) return a;return b;}
inline LL remin(LL a,LL b){if (a<b) return a;return b;}
inline LL remax(LL a,LL b){if (a>b) return a;return b;}

const int Maxn=1000;
const int Maxm=500000;
int Ans=0;

struct Edge{
	int v,f,nxt;
	Edge(){}
	Edge(int a,int b,int c){v=a;f=b;nxt=c;}
};
Edge e[Maxm+10];
int head[Maxn+10];
int nume=1;
inline void addEdge(int u,int v,int f){
	e[++nume]=Edge(v,f,head[u]);
	head[u]=nume;
	e[++nume]=Edge(u,0,head[v]);
	head[v]=nume;
	//printf("MakeEdge %d -> %d : %d\n",u,v,f);
}

int src;
int sink;

queue<int> que;
int dist[Maxn+10];
inline bool bfs(){
	while(!que.empty()) que.pop();
	memset(dist,-1,sizeof(dist));
	que.push(src);
	dist[src]=0;
	while(!que.empty()){
		int now=que.front();
		//printf("BFS->%d\n",now);
		que.pop();
		for (int i=head[now];i;i=e[i].nxt){
			//printf("BFS Try->%d\n",e[i].v);
			int tp=e[i].v;
			if (dist[tp]==-1 && e[i].f!=0){
				dist[tp]=dist[now]+1;
				que.push(tp);
			}
		}
	}
	return dist[sink]!=-1;
}

int dfs(int x,int delta){
	int ret=0;
	if (x==sink){
		return delta;
	}else{
		for (int i=head[x];i;i=e[i].nxt){
			int tp=e[i].v;
			if (dist[tp]==dist[x]+1 && e[i].f!=0){
				int dd=dfs(tp,remin(delta,e[i].f));
				e[i].f-=dd;
				e[i^1].f+=dd;
				delta-=dd;
				ret+=dd;
			}
		}
	}
	return ret;
}

inline int dinic(){
	int ret=0;
	while(bfs())ret+=dfs(src,Inf);
	return ret;
}

int n,m;
int score[35][35];
struct MapEdge{
	int x,y;
	int nxt;
	MapEdge(){}
	MapEdge(int a,int b,int c){x=a;y=b;nxt=c;}
};
MapEdge me[Maxm+10];
int MapNume=0;
int MapHead[35][35];
int inpoint[35][35];
bool visited[35][35];

inline void addMapEdge(int x,int y,int x1,int y1){
	me[++MapNume]=MapEdge(x1,y1,MapHead[x][y]);
	MapHead[x][y]=MapNume;
	inpoint[x1][y1]++;
}

inline int point2id(int x,int y){
	return (x-1)*m+y+1;
}

queue<pair<int,int> > MapQue;
inline void spfaR(int x,int y){
	//memset(visited,false,sizeof(visited));
	while(!MapQue.empty()) MapQue.pop();
	visited[x][y]=true;
	MapQue.push(make_pair(x,y));
	while(!MapQue.empty()){
		int nowx=MapQue.front().first,nowy=MapQue.front().second;
		//printf("TopSort->%d %d\n",nowx,nowy);
		MapQue.pop();
		for (int i=MapHead[nowx][nowy];i;i=me[i].nxt){
			int tx=me[i].x,ty=me[i].y;
			if (inpoint[tx][ty]<=1){
				addEdge(point2id(tx,ty),point2id(nowx,nowy),Inf);
				inpoint[tx][ty]--;
				visited[tx][ty]=true;
				MapQue.push(make_pair(tx,ty));
			}else{
				addEdge(point2id(tx,ty),point2id(nowx,nowy),Inf);
				inpoint[tx][ty]--;
			}
		}
		
	}
}

inline void solveRoundP(){
		for (int i=1;i<=n;i++){
			for (int j=1;j<=m;j++){
				if (inpoint[i][j]<=0 && visited[i][j]==false){
					spfaR(i,j);
				} 
			}
		}	
	
	for (int i=1;i<=n;i++){
		for (int j=1;j<=m;j++){
			if (inpoint[i][j]<=0){
				if (score[i][j]>=0){
					addEdge(src,point2id(i,j),score[i][j]);
					Ans+=score[i][j];
				}else{
					addEdge(point2id(i,j),sink,-score[i][j]);
				}
			}
			///Arrest
			//if (inpoint[i][j]==0)
				//printf("UsePoint->%d %d\n",i,j);
		}
	}
}


int main(){
	//freopen("pvz.in","r",stdin);
	//freopen("pvz.out","w",stdout);
	scanf("%d%d",&n,&m);
	src=1;
	sink=n*m+2;
	for (int i=1;i<=n;i++){
		for (int j=1;j<=m;j++){
			if (j>1) addMapEdge(i,j,i,j-1);
			scanf("%d",&score[i][j]);
			int w;
			scanf("%d",&w);
			for (int k=1;k<=w;k++){
				int x,y;
				scanf("%d%d",&x,&y);
				x++;y++;
				if (x!=i || y!=j-1) 
					addMapEdge(i,j,x,y);
			}
		}
	}
	solveRoundP();
	printf("%d\n",Ans-dinic());
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值