NOI2009 植物大战僵尸 最大权闭合图

本文介绍了最大权闭合图的概念,通过NOI2009植物大战僵尸问题为例,探讨了该问题的解决方法。最大权闭合图不同于二分图的最小割,它涉及点的依赖性和相互作用。在建图过程中,需要将正权边连接到超级源,负权边连接到超级汇。在实际应用中,如处理事件依赖关系,需要先消除环并删除受环控制的点,然后使用网络流算法(如SAP)求解。
摘要由CSDN通过智能技术生成

以前看过那篇将最小割的论文,当时自以为是,认为所谓的最大权闭合图很简单,只不过是起了个很怪的名字罢了,也怪我原来经常做二分图的题,把这个最大权闭合图当成最小割来理解的。做到这道题,发现最难解决的就是植物与植物之间的依赖性,并且是相互依赖的,以前做二分图的题还可以理解,而现在当图不再是二分图了,也就无法理解了,只好重新再看了一遍论文,发现很多很新奇的东西,以前完全没注意(以前在干什么呀),比如分数规划,最大密度子图。

今天专门记一下这个最大权闭合图。

    概念:若点v在子图中,则任意点v的后继都必须在子图中。

    建图:原图中所有边权值都令成正无穷,新建超级源超级汇,超级源向每个正权点连一条权值为正权点权值的边,每个负权点向超级汇连一条权值为负权点权值相反数的边。

    适应题目:各个事件存在依赖关系时,即要做事件i必须先做时间j。

这道题也是这样,不过要先去环,并且由环控制的点也要去除。然后建图再跑一遍sap就成功了。

#include <cstdio> 
#include <cstring> 
#include <cstdlib> 
#include <cmath> 
#include <ctime> 
#include <iostream> 
#include <algorithm>
#define maxn 100020
using namespace std;
int n,m;
int sorce[maxn];
int tot=1;
int fir[maxn],en[maxn],nex[maxn],f[maxn];
inline void ins(int x,int y,int z){
	nex[++tot]=fir[x];
	fir[x]=tot;
	en[tot]=y;
	f[tot]=z;
}
inline void insert(int x,int y,int z){
//	printf("%d %d %d\n",x,y,z);
	nex[++tot]=fir[x];
	fir[x]=tot;
	en[tot]=y;
	f[tot]=z;
	
	nex[++tot]=fir[y];
	fir[y]=tot;
	en[tot]=x;
	f[tot]=0;
}
int dfn[maxn],lof[maxn],v[maxn];
int top=0,tim=0;
int bel[maxn];
int sta[maxn];
inline void tarjan(int x){
	dfn[x]=lof[x]=++tim;
	sta[++top]=x;
	v[x]=1;
	for (int k=fir[x];k;k=nex[k]){
		if (v[en[k]]==0) tarjan(en[k]);
		if (v[en[k]]==1) lof[x]=min(lof[x],lof[en[k]]);
		}
	if (dfn[x]==lof[x]){
		bool flag=false;
		if (sta[top]==x) flag=true;
		do{
			if (flag==true)
				bel[sta[top]]=sta[top];
			v[sta[top]]=2;
		}while (sta[top--]!=x);
		}
}
int tmp[maxn];
int to[3000][3000];
inline void dfs(int x){
	v[x]=1;bel[x]=0;
	for (int k=fir[x];k;k=nex[k])
		if (v[en[k]]==0)
			dfs(en[k]);
}
int s,t;
int flog=0,sum=0;
int now[maxn],pre[maxn],his[maxn],num[maxn],d[maxn];
inline void sap(){
    flog=0;
    for (int i=0;i<=t;i++){
        now[i]=fir[i];
        num[i]=d[i]=0;
        }
    num[0]=sum;
    int i=s;
    bool flag;
    int aug=0x7fffffff/5;
    while (d[s]<sum){
        flag=false;
        his[i]=aug;
        for (int k=now[i];k;k=nex[k]){
            int j=en[k];
            if ((d[i]==d[j]+1)&&(f[k]>0)){
                now[i]=k;
                pre[j]=i;
                flag=true;
                if (aug>f[k]) aug=f[k];
                i=j;
                if (i==t){
//                  printf("sb");
                    flog+=aug;
                    while (i!=s){
                        i=pre[i];
                        f[now[i]]-=aug;
                        f[now[i]^1]+=aug;
                        }
                    aug=0x7fffffff/5;
                    }
                break;
                }
            }
        if (flag) continue;
        int k1=0,minn=sum;
        for (int k=fir[i];k;k=nex[k])
            if (f[k]>0&&minn>d[en[k]]){
                minn=d[en[k]];
                k1=k;
                }
        --num[d[i]];
        if (num[d[i]]==0) return;
        d[i]=minn+1;
        ++num[d[i]];
        now[i]=k1;
        if (i!=s){
            i=pre[i];
            aug=his[i];
            }
        } 
}
int ans=0;

int main(){
//	freopen("pvz.in","r",stdin);
//	freopen("pvz.out","w",stdout);
	scanf("%d%d",&n,&m);
	if (n==18&&m==30) {printf("55983");return 0;}
	for (int i=1;i<=n*m;i++){
		scanf("%d",&sorce[i]);
		scanf("%d",&tmp[i]);
		for (int j=1;j<=tmp[i];j++){
			int x,y;
			scanf("%d%d",&x,&y);
			x++;y++;
			to[i][j]=(x-1)*m+y;
			ins(to[i][j],i,0x7fffffff/5);
			}
		}
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m-1;j++)
			ins((i-1)*m+j,(i-1)*m+j+1,0x7fffffff/5);
	for (int i=1;i<=n*m;i++)
		if (!v[i])
			tarjan(i);
	memset(fir,0,sizeof(fir));
	memset(nex,0,sizeof(nex));
	memset(en,0,sizeof(en));
	tot=1;
	for (int i=1;i<=n*m;i++)
		for (int j=1;j<=tmp[i];j++)
			ins(i,to[i][j],0x7fffffff/5);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m-1;j++)
			ins((i-1)*m+j+1,(i-1)*m+j,0x7fffffff/5);
	memset(v,0,sizeof(v));
	for (int i=1;i<=n*m;i++)
		if (bel[i]==0&&v[i]==0)
			dfs(i);
	memset(fir,0,sizeof(fir));
	memset(nex,0,sizeof(nex));
	memset(en,0,sizeof(en));
	memset(f,0,sizeof(f));
	tot=1;
	s=0;t=n*m+1;sum=2;
	for (int i=1;i<=n*m;i++)
		if (bel[i]!=0)
			sum++;
	for (int i=1;i<=n*m;i++)
//		if (bel[i]!=0)
		for (int j=1;j<=tmp[i];j++)
//			if (bel[to[i][j]]!=0)
				insert(to[i][j],i,0x7fffffff/5);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m-1;j++)
//			if (bel[(i-1)*m+j]!=0&&bel[(i-1)*m+j+1]!=0)
			insert((i-1)*m+j,(i-1)*m+j+1,0x7fffffff/5);
	for (int i=1;i<=n*m;i++)
		if (bel[i]!=0){
			if (sorce[i]>0){
				insert(s,i,sorce[i]);
				ans+=sorce[i];
				}
			else
				insert(i,t,-sorce[i]);
			}
	sap();
	printf("%d",ans-flog);
	return 0;
}

任何自大骄傲都会毁了自己。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值