BZOJ 1565 NOI2009 植物大战僵尸(最大权闭合子图)

题目
可以发现,这个题就是在有一堆某个植物要在某个植物之前吃的要求下求最大值。
如果依赖关系是棵树,可以树形DP。
如果依赖关系是个DAG,可以做最大权闭合子图
具体来说,最大权闭合子图的求解是在网络流二元关系下实现的。
具体关系为某一个吃了后面的某一个没吃就不合法,代价为inf。
于是可以用inf流量的边将他们相连,正收益的与S相连,正代价的与T相连,将正收益全部相加-最小割(正代价+需要失去的正收益)就是答案。

AC Code:

#include<bits/stdc++.h>
#define maxn 601
#define maxm maxn*maxn*4
#define inf 0x3f3f3f3f
#define LL long long
using namespace std;

char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
void read(int &res)
{ 	char ch;bool f=0;for(;!isdigit(ch=getc());) if(ch=='-') f=1;
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
	(f) && (res = -res);}
int n,m,sc[maxn];
vector<int>G[maxn];
inline int id(int a,int b){ return (a-1)*m+b; }

int dfn[maxn],low[maxn],c[maxn],siz[maxn],sum[maxn],tot,scc;
int sta[maxn],tp;
void dfs(int now)
{
	dfn[now] = low[now] = ++tot;
	sta[tp++] = now;
	for(int i=0,siz=G[now].size(),v;i<siz;i++)
		if(!dfn[v=G[now][i]]) dfs(v),low[now]=min(low[now],low[v]);
		else if(!c[v]) low[now] = min(low[now],dfn[v]);
	if(low[now]==dfn[now])
	{
		scc++;
		for(int tmp=-1;tmp!=now;)
			c[tmp=sta[--tp]]=scc,
			siz[scc]++,
			sum[scc]+=sc[tmp];
		if(siz[scc] > 1)
			sum[scc] = -inf;
	}
}

int S,T,h[maxn],gap[maxn];
int buf[maxn],info[maxn],Prev[maxm],to[maxm],cap[maxm],cnt_e=1;
void Node(int u,int v,int c){Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c;}
void Line(int u,int v,int c){ Node(u,v,c),Node(v,u,0); }

/*
int aug(int now,int Max)
{
	if(now == T) return Max;
	int inc , st = Max;
	for(int &i=info[now];i;i=Prev[i])
		if(cap[i] && h[to[i]]+1 == h[now])
		{
			inc = aug(to[i],min(st,cap[i]));
			st-=inc,cap[i]-=inc,cap[i^1]+=inc;
			if(!st || h[S]>T) return Max-st;
		}
	if(!--gap[h[now]]) h[S] = T+1;
	++gap[++h[now]];info[now]=buf[now];
	return Max-st;
}*/

int aug(int now,int Max){
	if(now == T) return Max;
	int inc,st=Max;	
	for(int &i=info[now];i;i=Prev[i])if(cap[i]&&h[to[i]]+1==h[now]){
		inc=aug(to[i],min(cap[i],st));
		if(inc) st-=inc,cap[i]-=inc,cap[i^1]+=inc;
		else h[to[i]]=-1;
		if(!st) break;
	}
	return Max-st;
}

bool BFS()
{
	static int q[maxn],L,R;
	memset(h,-1,sizeof h);
	h[q[L=R=0]=T]=0,R++;
	for(int now;L<R;)
	{	now = q[L++];
		for(int i=info[now];i;i=Prev[i])
			if(cap[i^1]&&h[to[i]]==-1)
				h[q[R++]=to[i]]=h[now]+1;
	}
	return h[S] != -1;
}

int main()
{
	//freopen("1.in","r",stdin);
	read(n),read(m);
	for(int i=1,x;i<=n;i++)
		for(int j=1;j<=m;j++){	
			read(sc[id(i,j)]),read(x);
			for(int u,v;x--;)
				read(u),read(v),u++,v++,G[id(u,v)].push_back(id(i,j));
			if(j<m) G[id(i,j)].push_back(id(i,j+1));
		}
	for(int i=1;i<=n*m;i++)
		if(!dfn[i])
			dfs(i);
	S = scc+1 , T = scc+2;
	int ans = 0;
	for(int i=1;i<=scc;i++)
	{
		if(sum[i] > 0) Line(S,i,sum[i]),ans+=sum[i];
		else if(sum[i] < 0)Line(i,T,-sum[i]);
			//printf("%d\n",sum[i]);
		}
	for(int i=1;i<=n*m;i++)
		for(int j=0,siz=G[i].size(),v;j<siz;j++)
			if(c[v=G[i][j]] != c[i])
			{
				//printf("%d %d\n",c[i],c[v]);
				Line(c[i],c[v],inf);
			}
	//printf("%d\n",ans);
	memcpy(buf,info,sizeof info);
	for(;BFS();)
	{
		ans -= aug(S,inf);
		memcpy(info,buf,sizeof buf);
		if(ans < 0){ ans = 0;break; }
	}
	printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值