描述


http://www.lydsy.com/JudgeOnline/problem.php?id=1565

n*m的矩阵,可以种植植物,僵尸从图的右边进入吃植物.前面的植物可以保护后面的植物,还有一些植物会保护特定位置的其他植物.这里保护的意思是:a保护b等价于僵尸必须先吃a,才能吃b.每个植物有一个价值(可正可负),问僵尸能获得的最大价值.

1565: [NOI2009]植物大战僵尸

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1972  Solved: 917
[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 。

Source

分析


要吃一个植物必须要吃保护它的植物,很容易想到最大权闭合图,由被保护植物向保护它的植物连边.但此题特殊的地方在于可能形成环,也就是要吃掉这个植物必须先吃掉他自己,显然这是不可能的,所以我们可以用Tarjan先去掉强连通分量,然后递归把所有不能吃的植物保护的植物都标记掉.标记结束后把剩下能吃的植物与源点汇点,以及在彼此之间连边,跑最大流即可.

注意:

1.Dinic的dfs里回退建立反向弧的时候好几次都把g[e.to][e.rev]+=d写成了-=d.....查了一个小时都没发现.

ps.关于Tarjan.

1.对于图中的一个强连通分量,进入第一个点后,在回退出来之前,可以搜索到所有其他点,所以一个强连通分量一定在同一棵搜索子树中,并且如果在搜到的时候入栈的话,所有其他点都在第一个点的上面.对于极大的强连通分量,第一个点不可能的连到时间戳更小的点(因为当前已经极大,如果可以,时间戳更小的点一定能连到时间戳更大的点,则当前强连通分量就不是极大的了),所以第一个点能连到的最早的点是自己,而之后的强连通分量中的其他点都可以连到更早的点,更早的点可以连到更早更早的点,最后都能连到第一个点.当我们做Tarjan算法的时候,当遇到dfn==low的点,把栈中所有它上面的点都弹出,也就是清空以这个点为根的子树.根据这样的规则,当遇到dfn==low的点的时候,由于这个点的子节点都访问过了,也就是再次回退到了这个点,所以这个点能到达的最早的点是所有他的子节点和他自己能到达的最早的点中最小的,这样就可以知道以这个点为根的子树中的所有点能到的最早的点都不会早于这个点.所以这个点与比它早的点不连通.而此时子树中无法到达当前点的子树都已经弹出栈了,也就是栈中当前点之上的点都可以到达当前点,那么这些点就强连通,并且无法与更早或更晚的点再连通,所以是极大的.

2.关于时间戳.我之前一直写的是把时间戳作为dfs的参数,也就是某点x的时间戳为idx,则它的所有子节点都是idx+1,这样写对不对?

是错误的!

当找到dfn==low时,栈上方的点都可以追溯到当前点的时间戳,如果就是当前点,大丈夫.如果是其他与当前点同一级的点,那个点还没有出栈,说明它能到更早的地方,然后通过更早的地方就能到当前点,这样仍是强连通分量,大丈夫.但是此时我们只统计了当前点的子树,扔掉了帮助过我们的那个和当前点同一级的点,这样强连通分量就不是极大的了.比如 1->2,  2->3, 3->1, 1->4, 4->2. 如果按我之前的写法就会认为1,2,3强连通,而4与自己强连通,但实际上这四个点都是强连通的.

不过!但是!

如果把

else if(instack[u])
{
low[v]=min(low[v],dfn[u]);
}

改成

else if(instack[u])
{
low[v]=min(low[v],low[u]);
}

就又正确了.为什么呢?

之前的写法是追溯到当前点为根的子树所能直接到达的点,也就是说如果不是当前点x直接到达的,就是它的子节点直接到达的,或者子节点的子节点...总之是这个子树中某个点直接到达的.这就会出现之前所说的错误,导致强连通分量不是极大的.但如果改成第二种写法,那当前点的low就是子树中的点能够直接或间接到达的最早的点,也就是一下找到头了.这样一来,找到dfn==low的时候,同一时间戳的其他点还存在,说明这些点还能到更早的点,那连向这些点的low值应该更早(一下找到最早最早),而现在栈中的点最早能到达当前点的时间戳,而不是更早,说明这些点连的不是其他点而是当前点,所以强连通分量一定是极大的.或者可以这样理解,最早只能到当前点的时间戳,子树的外面是强连通的(已经处理完了),无法跑到子树的上面也就是无法跑到子树的外面去,所以只能在子树内强连通.

感觉以后还是写得规范一点吧.

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<iostream>
#define rep(i,n) for(int i=0;i<(n);i++)
#define for1(i,a,n) for(int i=(a);i<=(n);i++)
#define read(a) a=getnum()
#define CC(i,a) memset(i,a,sizeof(i))
using namespace std; const int maxn=(*)+,INF=0x7fffffff;
int n,m,W,N;
int w[maxn];
bool mark[maxn];
vector <int> e[maxn]; inline int getnum() { int r=,k=;char c;for(c=getchar();c<''||c>'';c=getchar()) if(c=='-') k=-; for(;c>=''&&c<='';c=getchar()) r=r*+c-''; return r*k; } struct Tarjan
{
int idx;
int dfn[maxn],low[maxn];
bool ins[maxn];
stack <int> s; void dfs(int u)
{
s.push(u);
ins[u]=true;
dfn[u]=low[u]=idx++;
rep(i,e[u].size())
{
int v=e[u][i];
if(!dfn[v])
{
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v])
{
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u])
{
ins[u]=false;
if(s.top()==u) s.pop();
else
{
int top;
while((top=s.top())!=u)
{
s.pop();
ins[top]=false;
mark[top]=true;
}
s.pop();
mark[top]=true;
}
}
} }tarjan; struct Flow
{
int level[maxn],iter[maxn];
struct edge
{
int to,cap,rev;
edge() {}
edge(int a,int b,int c):to(a),cap(b),rev(c) {}
};
vector <edge> g[maxn]; void add_edge(int from,int to,int cap)
{
g[from].push_back(edge(to,cap,g[to].size()));
g[to].push_back(edge(from,,g[from].size()-));
} void bfs(int s)
{
CC(level,-);
level[s]=;
queue <int> q;
q.push(s);
while(!q.empty())
{
int t=q.front(); q.pop();
rep(i,g[t].size())
{
edge e=g[t][i];
if(e.cap>&&level[e.to]<)
{
level[e.to]=level[t]+;
q.push(e.to);
}
}
}
} int dfs(int u,int t,int f)
{
if(u==t) return f;
for(int &i=iter[u];i<g[u].size();i++)
{
edge &e=g[u][i];
if(e.cap>&&level[e.to]>level[u])
{
int d=dfs(e.to,t,min(f,e.cap));
if(d>)
{
e.cap-=d;
g[e.to][e.rev].cap+=d;
return d;
}
}
}
return ;
} int max_flow(int s,int t)
{
int flow=;
bfs(s);
while(level[t]>)
{
int f;
CC(iter,);
while((f=dfs(s,t,INF))>) flow+=f;
bfs(s);
}
return flow;
} }flow; void pro(int u)
{
if(mark[u]) return;
mark[u]=true;
rep(i,e[u].size())
{
pro(e[u][i]);
}
} void solve()
{
for1(i,,N)
{
if(!tarjan.dfn[i])
{
tarjan.dfs(i);
}
}
for1(i,,N)
{
if(mark[i])
{
rep(j,e[i].size())
{
pro(e[i][j]);
}
}
}
for1(i,,N)
{
if(mark[i]) continue;
if(w[i]>)
{
W+=w[i];
flow.add_edge(,i,w[i]);
}
else
{
flow.add_edge(i,N+,-w[i]);
}
rep(j,e[i].size())
{
flow.add_edge(e[i][j],i,INF);
}
}
printf("%d\n",W-flow.max_flow(,N+));
} void init()
{
read(n); read(m); N=n*m;
for1(i,,n)
{
for1(j,,m)
{
int u=(i-)*m+j,a;
read(w[u]); read(a);
while(a--)
{
int x,y;
read(x); read(y);
int v=x*m+y+;
e[u].push_back(v);
}
if(j>)
{
e[u].push_back(u-);
}
}
}
} int main()
{
#ifndef ONLINE_JUDGE
freopen("plant.in","r",stdin);
freopen("plant.out","w",stdout);
#endif
init();
solve();
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
system("plant.out");
#endif
return ;
}

BZOJ_1565_[NOI2009]_植物大战僵尸_(Tarjan+最大流+最大权闭合图)的更多相关文章

  1. BZOJ 1565 NOI2009 植物大战僵尸 topo+最小割(最大权闭合子图)

    题目链接:https://www.luogu.org/problemnew/show/P2805(bzoj那个实在是有点小小的辣眼睛...我就把洛谷的丢出来吧...) 题意概述:给出一张有向图,这张有 ...

  2. BZOJ_1497_[NOI2006]_最大获利_(最大流+最大权闭合图)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1497 共n个站点,给出建立每个站点所需要的花费.现在有m个客户需要开通服务,每个客户需要有两个 ...

  3. b2OJ_1565_[NOI2009]植物大战僵尸_拓扑排序+最大权闭合子图

    b2OJ_1565_[NOI2009]植物大战僵尸_拓扑排序+最大权闭合子 题意:n*m个植物,每个植物有分数(可正可负),和能保护植物的位置.只能从右往左吃,并且不能吃正被保护着的,可以一个不吃,求 ...

  4. 【BZOJ1565】【NOI2009】植物大战僵尸(网络流)

    [BZOJ1565][NOI2009]植物大战僵尸(网络流) 题面 BZOJ 洛谷 题解 做了这么多神仙题,终于有一道能够凭借自己智商能够想出来的题目了.... 好感动. 这就是一个比较裸的最小割模型 ...

  5. Bzoj 1565: [NOI2009]植物大战僵尸 最大权闭合图,拓扑排序

    题目: http://cojs.tk/cogs/problem/problem.php?pid=410 410. [NOI2009] 植物大战僵尸 ★★★   输入文件:pvz.in   输出文件:p ...

  6. 【bzoj1565】[NOI2009]植物大战僵尸 拓扑排序+最大权闭合图

    原文地址:http://www.cnblogs.com/GXZlegend/p/6808268.html 题目描述 输入 输出 仅包含一个整数,表示可以获得的最大能源收入.注意,你也可以选择不进行任何 ...

  7. tyvj P1135 - 植物大战僵尸 最大权闭合图

    P1135 - 植物大战僵尸 From ytt    Normal (OI)总时限:10s    内存限制:128MB    代码长度限制:64KB 背景 Background 虽然这么多天了,,虽然 ...

  8. [bzoj1565][NOI2009]植物大战僵尸_网络流_拓扑排序

    植物大战僵尸 bzoj1565 题目大意:给你一张网格图,上面种着一些植物.你从网格的最右侧开始进攻.每个植物可以对僵尸提供能量或者消耗僵尸的能量.每个植物可以保护一个特定网格内的植物,如果一个植物被 ...

  9. bzoj 1565 [NOI2009]植物大战僵尸【tarjan+最大权闭合子图】

    一上来以为是裸的最大权闭合子图,上来就dinic -然后没过样例.不得不说样例还是非常良心的给了一个强连通分量,要不然就WA的生活不能自理了 然后注意到有一种特殊情况:每个植物向他保护的植物连边(包括 ...

随机推荐

  1. 学习笔记_过滤器应用(粗粒度权限控制(拦截是否登录、拦截用户名admin权限))

    RBAC ->基于角色的权限控制 l  tb_user l  tb_role l  tb_userrole l  tb_menu(增.删.改.查) l  tb_rolemenu 1 说明 我们给 ...

  2. Visual C++ 打印编程技术-编程基础-获取打印机

    标准方法是用: EnumPrinters() 函数获取 #define PRINTER_ENUM_DEFAULT 0x00000001 #define PRINTER_ENUM_LOCAL 0x000 ...

  3. JavaSE生成随机数

    今天呢,老师讲了一下怎么用jvm实现随机数(本人对此很感兴趣),一个简单的随机100以内整数的代码如下: /** 生成随机数 */ import java.util.Random; public cl ...

  4. Get AD user 的三种方法

    一. 通过AccountManagement 程序集(System.DirectoryServices.AccountManagement) acountManagement 包含有: 1. User ...

  5. 记:mysql 连接超时解决办法

    错误描述:Timeout in IO operation 原连接字符串为:Server=182.180.50.118;port=3306;Database=test;Uid=root;Pwd=123; ...

  6. 解决Windows+Eclipse+Python错误: SyntaxError: Non-ASCII character

    这个问题出现的原因是在文中使用了中文或者其他的非英文字符 最简单的解决办法: 在文件的第一行加上编码表示,切记:一定要是第一行,而且在前面没有任何对文件和项目的注释. 也就是说在Windows Ecl ...

  7. python 自动化之路 day 面向对象基础

    1.面向對象基础概述 面向过程: 根据业务逻辑从上到下垒代码(如果程序修改,对于依赖的过程都需要进行修改.) 函数式: 将某功能代码封装到函数中,如后便无需重复编写,仅需要调用函数即可 面向对象: 世 ...

  8. ubuntu thinkphp pathinfo 404等问题

    这个问题 困扰了我一天,由于对nginx的配置文件中的各种变量不懂.配置起来很麻烦,从网上搜索的,感觉合适自己的不多!!! 找啊找啊..终于找一篇!!!! 我的环境: php ubuntu 12.04 ...

  9. REST接口规范

    参考文章 这篇文章使用不同的method代表不同操作 http://www.cnblogs.com/tommyli/p/3913018.html 实际应用中(我们过去的应用) 则是直接使用url来代表 ...

  10. 服务端生成word并压缩打包下载

    所需工具 phpwrod 库 php_zip 扩展 下载phpword库,放到类加载路径. 安装php_zip扩展 下载地址 http://pecl.php.net/package/zip linux ...