BZOJ 1565 (NOI 2009) 最大权闭合子图

1565: [NOI2009]植物大战僵尸

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 2447   Solved: 1128
[ Submit][ Status][ Discuss]

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 。



        膜拜曾经参加过NOI的大牛Orz……
        题面还是很有意思的,当时很火的植物大战僵尸,想当年这个游戏可是充斥着我的整个小学生活的。也几乎是因为这个我荒废了学习……
        说起网络流的话我自认为很拿手,因为在高中阶段我破题最多应该就是网络流(再一次GDKOI里我写出了网络流,别提多开心)。以致于到后面什么图论题都会想办法往网络流方向上靠。但是,老实说,这题这样的模型我还是第一次见。
        经过学习,可以知道,这一类问题就是最大权闭合子图问题,即给出一些点,每个点有点权(有正有负),求一个闭合子图,使得子图内点的权值和最大。其中,闭合图定义为,对于u∈闭合图,若有边<u,v>,那么v∈闭合图。很显然,本题基本符合这一模型,那么我们就具体来说说这一类问题的方法。
        这个问题当然又是有国家集训队的大牛写的论文的,具体见 胡伯涛《最小割模型在信息学竞赛中的应用》Orz……
        我们的做法是在原图的基础上构造一个新图,令源点连接所有点权为正的点流量即为点权,所有点权为负的点连接汇点流量取点权的相反数。对这样一个图求最小割,那么,最大权闭合子图的权值=正点权值和-最小割。问题就得到了解决,具体的证明看论文吧,其实也挺好理解的,我大致说说证明的思路。


        首先定义简单割:满足所有割中的所有边都与源点s或者汇点t相连的割。
        那么,可以证明,在新图中最小割一定是简单割,同时简单割分出来的、还有源点s的点集={s}U{闭合子图}。接着,我们对图的权值进行表示。简单割的权值(c1)=s与割另一边权值为正的点权值和(s1)+t与割另一边负的点权绝对值和(s2)。然后闭合子图的点权值和(c2)=闭合子图中正点权值和(s3)-闭合子图中负点权绝对值和(s2)。二者相加得c1+c2=s1+s2+s3-s2=s1+s3=所有正点权值和(s4),进行变形得c2=s4-c1。由于s4是定值,所以显然,当c1最小即c1为最小割的时候s4(闭合子图点权和)最大。故"最大权闭合子图的权值=正点权值和-最小割"得证。
        看证明的时候时刻注意每一个概念的含义,然后这里的每一个定理和引理都是适用于我们自己新建的图的,并不一定适用于所有的图。我自己看证明的时候曾经忽略了这个,然后就觉得证明有误,可以找出反例,但是实际上在规定的图中这些定理都是满足的。


        然后关于本题,如果说有先决条件(u,v),那么我们连边并不是想当然的连接(u,v),而是连接(v,u)。这个的几何意义在于,如果要走到v(流经v),那么也一定要流经u(流量守恒原理)。有多个先决条件的情况也是如此,根据求最大流的算法,一定会尽量的满足所有先决条件,当无法满足时(负权和大于正权),又因流量限制会使得最后的结果仍让是非负的。好吧我都快把自己给说晕了,反正就是那个意思。
        除此之外,本体还可能不是一个DAG,所以在这之前我们要先进行tarjan。即如果存在某个连通分量中点的个数大于1,那么次连通分量内的所有点都无法到达,在建立新的流网络时就不把这个点加入。整个过程具体见代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<vector>
#include<queue>
#define INF 2e9
#define LL long long
#define MAX_V 1000

using namespace std;

namespace Dinic
{
	struct Edge
	{
		int dest,cap;
		Edge *next,*pair;
		Edge() {}
		Edge(int _dest,int _cap,Edge* _next):dest(_dest),cap(_cap),next(_next){}
	};
	
	Edge *e[MAX_V],*c[MAX_V],*p[MAX_V];
	int Stack[MAX_V],Queue[MAX_V],dist[MAX_V];
	int s,t,top;
	long long maxflow;
		
	inline void addedge(int a,int b,int c)
	{
		e[a]=new Edge(b,c,e[a]);
		e[b]=new Edge(a,0,e[b]);
		e[a]->pair=e[b],e[b]->pair=e[a];
	}
	bool lab()
	{
		memset(dist,0x3f,sizeof(dist));
		dist[s]=0; Queue[1]=s;
		for (int l=1,r=1;l<=r;l++)
		{
			int i=Queue[l];
			for (Edge *j=e[i];j;j=j->next)
			if (j->cap && dist[j->dest]>dist[i]+1)
			{
				dist[j->dest]=dist[i]+1;
				if (j->dest==t) return 1;
				Queue[++r]=j->dest;
			}
		}
		return 0;
	}
	
	inline void aug()
	{
		int top=0; Stack[++top]=s;
		memcpy(c,e,sizeof(e));
		while (top)
		{
			int i=Stack[top];
			if (i!=t)
			{
				Edge *j;
				for (j=c[i];j;j=j->next)
					if (j->cap && dist[j->dest]==dist[i]+1) break;
				if (j) top++,Stack[top]=j->dest,c[i]=p[top]=j;
				else top--,dist[i]=INF;
			} else
			{
				int delta=INF;
				for (int i=top;i>=2;i--)
					delta=std::min(delta,p[i]->cap);
				maxflow+=delta;
				for (int i=top;i>=2;i--)
				{
					p[i]->cap-=delta,p[i]->pair->cap+=delta;
					if (!p[i]->cap) top=i-1;
				}
			}
		}
	}
	
	long long dinic()
	{
		maxflow=0;
		while (lab()) aug();
		return maxflow;
	}
}

int sorce[MAX_V],dfn[MAX_V],low[MAX_V],cnt[MAX_V],belong[MAX_V];
int n,m,dindex=0,bcnt;
vector<int> g[MAX_V];
deque<int> stack;
bool v[MAX_V];

inline void tarjan(int dep)
{
	v[dep]=1;
	dfn[dep]=low[dep]=++dindex;
	stack.push_back(dep);
	for(int i=0;i<g[dep].size();i++)
	{
		int ff=g[dep][i];
		if (!dfn[ff])
		{
			tarjan(ff);
			low[dep]=min(low[dep],low[ff]);
		} else if (v[ff]) low[dep]=min(low[dep],dfn[ff]);
	}
	if (low[dep]==dfn[dep])
	{
		bcnt++; int ff=0;
		while (ff!=dep)
		{
			ff=stack.back();
			stack.pop_back();
			belong[ff]=bcnt;
			v[ff]=0,cnt[bcnt]++;
		}
	}
}

inline void dfs(int dep)
{
	v[dep]=1;
	for(int i=0;i<g[dep].size();i++)
		if (!v[g[dep][i]]) dfs(g[dep][i]);
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n*m;i++)
	{
		int k;
		scanf("%d%d",&sorce[i],&k);
		for(int j=0;j<k;j++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			g[i].push_back(x*m+y);
		}
		int x=i/m,y=i%m;
		if (y>0) g[i].push_back(i-1);
	}
	for(int i=0;i<m*n;i++)
		if (!dfn[i]) tarjan(i);			tarjan求连通分量
	memset(v,0,sizeof(v));
	for(int i=0;i<m*n;i++)
		if (cnt[belong[i]]>1&&!v[i]) dfs(i);	把形成环的点标记为不可到达
	int s=Dinic::s=n*m;
	int t=Dinic::t=s+1;
	long long ans=0;
	for(int i=0;i<m*n;i++)
		if (!v[i])
		{
			if (sorce[i]>0) Dinic::addedge(s,i,sorce[i]),ans+=sorce[i];	正点权的边
			if (sorce[i]<0) Dinic::addedge(i,t,-sorce[i]);			负点权的边
			for(int j=0;j<g[i].size();j++)
				if (!v[j]) Dinic::addedge(g[i][j],i,INF);		先决关系
		}
	printf("%lld\n",ans-Dinic::dinic());			Dinic求最大流最小割,ans-最小割即为结果
}

         这里我用了Dinic模板,时间大概只用的300MS+,然而改成之前自己写的普通宽搜增广路算法,时间一下就变成了2000MS+,果然Dinic大法好!

        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值