植物大战僵尸

link

挺有意义的一道题,把最大权闭合子图和拓扑排序结合了起来。

问题可以分成两个部分。可以如此描述:每个点可以被攻击当且仅当所有可以保护它的点(认为右边的点也可以保护它)都被攻击,点有点权,求最大选择方案。把每个可以保护某个点的点作为这个点的后继,那么问题就成了选择这个点就一定要选择它的后继(反之是不成立的,因为选择了某个点不一定要选择它保护的点也不一定有能力去选择),于是就是一个最大权闭合子图问题。但发现样例都过不了,原因是假如存在一个强连通分量,这个分量中任意一个节点都是不可被攻击的,而且所有被分量中节点保护的节点也是不可被攻击的。不是很有必要tarjan找它我也忘了那玩意怎么写了,直接拓扑排序即可,因为拓扑排序的流程就保证了一个点的前驱不进队它自己也不可能进队,这就满足题意了。

写法上虽说两次用到的边是相反的,但没必要加两次边,因为网络流建边本就建立了正反两条,利用反的那一条即可(看边权是否为0)。一开始没有把问题想得特别清楚导致很是调了一会,就很傻逼。

code

#include<bits/stdc++.h>
//#define feyn
const int N=35;
const int S=800;
const int M=5e6;
const int maxn=1e9;
using namespace std;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}

struct edge{
	int t,v,next;
}e[M];
int head[S],esum=1;
inline void adde(int fr,int to,int val){
	e[++esum]=(edge){to,val,head[fr]};head[fr]=esum;
}
inline void add(int fr,int to,int val){
	adde(fr,to,val);adde(to,fr,0);
}

int m,n,a[N][N],ss,tt,cnt,c[S];

queue<int>qq;int d[S];bool in[S];
int t[S],nt,ll,rr,q[S];
inline bool check(){
	t[q[ll=rr=1]=ss]=++nt;d[ss]=1;
	while(ll<=rr){
		int wh=q[ll++];
		for(int i=head[wh],th;i;i=e[i].next){
			if(e[i].v==0||t[th=e[i].t]==nt)continue;
			q[++rr]=th;d[th]=d[wh]+1;t[th]=nt;
		}
	}
	return t[tt]==nt;
}
inline int dinic(int wh,int val){
	if(wh==tt)return val;int used=0;
	for(int i=head[wh],th;i;i=e[i].next){
		if(e[i].v==0||d[th=e[i].t]!=d[wh]+1)continue;
		int now=dinic(th,min(val,e[i].v));
		if(now)val-=now,used+=now,e[i].v-=now,e[i^1].v+=now;
		if(val==0)break;
	}
	if(val)d[wh]=-1;return used;
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);
	for(int i=0;i<m;i++)for(int j=0;j<n;j++)a[i][j]=++cnt;
	int s1,s2,s3;
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){
			read(c[a[i][j]]);read(s1);
			if(j!=n-1)add(a[i][j],a[i][j+1],maxn),d[a[i][j]]++;
			while(s1--){
				read(s2);read(s3);
				add(a[s2][s3],a[i][j],maxn),d[a[s2][s3]]++;
			}
		}
	}
	for(int i=1;i<=cnt;i++)if(d[i]==0)qq.push(i);
	while(!qq.empty()){
		int wh=qq.front();qq.pop();in[wh]=true;
		for(int i=head[wh],th;i;i=e[i].next){
			if(e[i].v!=0)continue;
			d[th=e[i].t]--;
			if(d[th]==0)qq.push(th);
		}
	}
	ss=++cnt,tt=++cnt;int ans=0;
	for(int i=1;i<=cnt-2;i++){
		if(in[i]==false)continue;
		if(c[i]>0)ans+=c[i],add(ss,i,c[i]);
		else add(i,tt,-c[i]);
	}
	while(check())ans-=dinic(ss,maxn);
	printf("%d\n",ans);
	
	return 0;
}
posted @ 2022-07-17 17:36  Feyn618  阅读(15)  评论(0编辑  收藏  举报