NOI2009植物大战僵尸

因为昨天做了一道最大获利,所以感觉思路差不多

对于每个植物,首先思考如果它们score都是整数的时候,贪心就好了,可以到达的就攻击

然而植物的score有负数,所以植物就可以分为获得收益的和付出代价的

这样大体思路就是分为两部分,代价连源点,收益连汇点,中间有保护关系的连不可删边,再跑个最小割

然而这个题的细节还是让我这个菜鸡一遍爆0

首先,显而易见的保护关系是攻击型的植物对攻击位置的保护关系

但是还有一些保护关系,例如想要攻击左边的植物就必须除掉右边的植物,所以还有这层隐藏保护关系

所以这些保护关系就能构成一个图,但这个图并不是一个DAG,环上的点以及环上的点都是不可除掉的点,这些点必须处理出来,不能放到网络流图中(样例还是比较良心的,能让人发现必须处理这种情况)

还有对于这类互相保护求最大收益跑最小割的问题,作为一个菜鸡我理解的也不是很透,但是可以发现,例如原图中关系为a保护b,b保护c,如果不直接处理成直接关系,从a向c连边,是会出问题的(这样的话边数会比较多,然而看一看数据范围这么小,NOI的题数据范围这么小肯定是有原因的啊)

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#define INF ((int)1e9)
#define fr(i,s,t) for(i=s;i<=t;i++)
using namespace std;
struct edge{
	int to,cap,flow;
};
vector <edge> edges;
vector <int> vec1[3000],vec[3000];
int a[101][101],n,m,degree[3000],N,T,m1,ans,d[3000],cur[3000];
bool f[3000],vis[3000],dis[601][601];
int read(){
	int x=0,w=1; char ch=0;
	while (ch<'0'||ch>'9'){
	  w=(ch=='-')?-1:1;
	  ch=getchar();
	}
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w*x; 
}
void  init(){
	n=read(); m=read();
	int i,j,k,num,x,y;
	fr(i,1,n) fr(j,1,m){
			N++;
			fr(k,1,m) if (j-k>=1) vec1[N].push_back(N-k);
			a[i][j]=read(); num=read();
			fr(k,1,num){
				x=read(); y=read();
				x++; y++;
				vec1[N].push_back((x-1)*m+y);
			}
		}		
}//建保护关系的原图 
void dfs(int x){
	vis[x]=1;
	int i,y;
	for (i=0;i<vec1[x].size();i++){
		y=vec1[x][i];
		if (vis[y]) continue;
		dfs(y);
	}
}
void work(){
	int i,j;
	fr(i,1,N){
		memset(vis,0,sizeof(vis));
		dfs(i);
		fr(j,1,N) if (vis[j]&&i!=j)
		  dis[i][j]=1,degree[j]++;
	}
}//处理保护关系 
void topo(){//处理不可达点 
	int i,x,y,j;
	work();
	memset(f,1,sizeof(f));
	queue <int> Q;
	fr(i,1,N) 
		if (!degree[i])
	    	f[i]=0,Q.push(i);
	while (!Q.empty()){
	  	x=Q.front(); Q.pop();
	  	for (y=1;y<=N;y++){
	  		if (!dis[x][y]) continue; 
	  		if (!f[y]) continue;
	  		degree[y]--;
	  		if (!degree[y]) Q.push(y),f[y]=0;
	  	}
	}
	fr(i,1,N) if (f[i])
	    fr(j,1,N) if (dis[i][j])
	        f[j]=1;
}
//处理保护关系原图 
void addedge(int x,int y,int cap){
	edges.push_back((edge){y,cap,0});
	edges.push_back((edge){x,0,0});
	m1+=2;
	vec[x].push_back(m1-2);//mdzzm1写成m了 
	vec[y].push_back(m1-1);
}
void build(){
	int i,j,num=0,y;
		
	fr(i,1,N) 
	  if (!f[i]) 
	    if (a[(i-1)/m+1][(i-1)%m+1]>0) 
		  ans+=a[(i-1)/m+1][(i-1)%m+1];
	T=2*N+1; m1=0;
	
	fr(i,1,n) fr(j,1,m){
		num++;   
		if (f[num]) continue;
		if (a[i][j]>0) addedge(N+num,T,a[i][j]);
		if (a[i][j]<0) addedge(0,num,-a[i][j]);
	}
	fr(i,1,N)
	  if (!f[i])
		for (y=1;y<=N;y++){
			if (f[y]) continue;
			if (!dis[i][y]) continue;
			addedge(i,y+N,INF);
		}
}//建网络流图 
bool bfs(){
	memset(d,0,sizeof(d));
	queue <int> Q;
	Q.push(0);
	int x,i,y,num;
	
	while (!Q.empty()){
		x=Q.front(); Q.pop();
		for (i=0;i<vec[x].size();i++){
			num=vec[x][i];
			if (edges[num].cap==edges[num].flow) continue;
			y=edges[num].to;
			if (y&&!d[y]){
				d[y]=d[x]+1;
				Q.push(y);
			}
		}
	}
	return d[T];
}
int dfs(int x,int a){
	if (x==T||a==0) return a;
	int num,y,flow=0,f;
	
	for (;cur[x]<vec[x].size();cur[x]++){
		num=vec[x][cur[x]];
		y=edges[num].to;
		if (d[x]+1==d[y]&&(f=dfs(y,min(a,edges[num].cap-edges[num].flow)))>0){
			edges[num].flow+=f;
			edges[num^1].flow-=f;
			flow+=f;
			a-=f;
			if (a==0) break;
		}
	}
	return flow;
}
int main(){
	init();
	topo();
	build();
	while (bfs()) 
	  	memset(cur,0,sizeof(cur)),ans-=dfs(0,INF);
	cout<<ans;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值