[Luogu 2805]NOI2009 植物大战僵尸

<题目链接>

这题是个比较经典的最大权闭合子图,可以建图转化为最小割问题,再根据最大流最小割定理,采用任意一种最大流算法求得。

对于每个点,如果点权w为正,则从源点到这个点连一条边权为w的有向边;否则如果w为负则从这个点到汇点连一条有向边。加边的时候预处理出反图的每个点入度。

其次,每一个被保护的点到保护它的点连一条边权为INF的有向边。

注意同一行的左侧点受到右侧点的间接保护,因此对于每一个不位于当前行最右的点,都要向其右侧的一个点连一条INF有向边(连一条即可,不必连所有)。

初始化完成后,在反向图(惯用链式前向星的表示,在跑最小割之前,边权为0的边就是反向图的边)上拓扑排序,删去在环中的点,留下有效点。

然后在建的这个图上跑最小割。

最终的答案为:源点到「与源点相连的所有有效点」的边权和减去网络最小割。

建模是个好能力,希望包括我在内的每一个OIer都能通过不懈努力获得。

加油。

#include <algorithm>
#include <climits>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN=10000,MAXM=900000;
bool exist[MAXN]/*拓扑排序有效点标记*/,vis[MAXN];
int n,m,N,S,T,cnt,ans,head[MAXN],cur[MAXN]/*当前弧*/,inn[MAXN]/*反图入度*/,dis[MAXN]/*层次图*/;
struct edge
{
    int nxt,to,w;
}e[MAXM];
void AddEdge(int x,int y,int w)
{
    e[++cnt].nxt=head[x];
    e[cnt].to=y;
    e[cnt].w=w;
    head[x]=cnt;
}
void AddEdges(int x,int y,int w)
{
    AddEdge(x,y,w);
    AddEdge(y,x,0);
    ++inn[x];//预处理反图入度
}
void T_Sort(int S)
{
    queue<int> q;//栈或队列都可以(优先队列或者手写堆其实也没问题)
    for(int i=S;i<=T;++i)
        if(!inn[i])
            q.push(i);//入度为0的入队
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        exist[x]=1;
        for(int i=head[x],t;i;i=e[i].nxt)
            if(!e[i].w && !--inn[t=e[i].to])
                q.push(t);
    }
    for(int i=head[S];i;i=e[i].nxt)
        if(!inn[e[i].to])
            ans+=e[i].w;//事先累加源点到「与源点相连的所有有效点」的边权
}
bool BFS(int S)
{
    queue<int> q;
    memset(dis,0,sizeof dis);
    memset(vis,0,sizeof vis);
    q.push(S);
    vis[S]=1;
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=head[x],t;i;i=e[i].nxt)
            if(e[i].w && exist[t=e[i].to] && !vis[t])
            {
                vis[t]=1;
                dis[t]=dis[x]+1;
                q.push(t);
            }
        if(vis[T])//已经处理到汇点就退出
            return 1;
    }
    return 0;
}
int DFS(int x,int k)
{
    if(x==T)
        return k;
    int tmp=0;//这个累加必须有,否则最后一个点会被卡掉。
    for(int i=cur[x],t,f;i;i=e[i].nxt)
        if(e[i].w && exist[t=e[i].to] && dis[t]==dis[x]+1 && (f=DFS(t,min(k,e[i].w))))
        {
            cur[x]=i;
            e[i].w-=f;//当前边的边权
            e[((i-1)^1)+1].w+=f;//反向边的边权
            tmp+=f;
            k-=f;
        }
    if(!tmp)
        dis[x]=-1;//必须有的操作
    return tmp;
}
void Dinic(void)
{
    int f;
    while(BFS(S))//预处理层次图,直到无法间出层次图
        while(memcpy(cur,head,sizeof cur),f=DFS(S,INT_MAX))//跑DFS直到当前层次图上无法增广。
            ans-=f;//连接源点的边权和减去最小割
}
int main(int argc,char *argv[])
{
    scanf("%d %d",&n,&m);
    N=n*m,T=N+1;//N是总点数,T是汇点
    for(int i=1,w,k;i<=N;++i)
    {
        scanf("%d %d",&w,&k);
        if(i%m)//如果不是当前行最后一个点,就向右连一条边。
            AddEdges(i,i+1,INT_MAX);
        if(w>0)
            AddEdges(S,i,w);//连源点
        else if(w<0)
            AddEdges(i,T,-w);//连汇点
        for(int j=1,r,c;j<=k;++j)
        {
            scanf("%d %d",&r,&c);
            AddEdges(r*m+c+1,i,INT_MAX);//保护关系
        }
    }
    T_Sort(S);//拓扑排序
    Dinic();//最小割
    printf("%d",ans);
    return 0;
}

谢谢阅读。

时间: 2024-10-14 06:27:25

[Luogu 2805]NOI2009 植物大战僵尸的相关文章

NOI2009植物大战僵尸

这题应该分两步来做: 1.拓扑排序,去掉无敌点 2.求最大闭合子图 需要注意几点: 1.拓扑排序时,如果(i,j)可以攻击到(x,y),那么增加(x,y)的入度,而不是(i,j)的入度 因为入度代表着要攻击它需要事先攻击几个点 2.求最大闭合子图时,用所有的正权点-最大流 3.求最大闭合子图时,如果(i,j)可以攻击到(x,y),那么连一条边(x,y)到(i,j),容量为正无穷 因为在最大闭合子图中边(x,y)到(i,j)意味着选(x,y)就必须要选(i,j),这与实际含义相符 4.s到正权点,

COGS410. [NOI2009] 植物大战僵尸

410. [NOI2009] 植物大战僵尸 ★★★   输入文件:pvz.in   输出文件:pvz.out   简单对比时间限制:2 s   内存限制:512 MB [问题描述] Plants vs. Zombies(PVZ)是最近十分风靡的一款小游戏.Plants(植物)和Zombies(僵尸)是游戏的主角,其中Plants防守,而Zombies进攻.该款游戏包含多种不同的挑战系列,比如Protect Your Brain.Bowling等等.其中最为经典的,莫过于玩家通过控制Plants来

图论(网络流):COGS 410. [NOI2009] 植物大战僵尸

410. [NOI2009] 植物大战僵尸 ★★★   输入文件:pvz.in   输出文件:pvz.out   简单对比时间限制:2 s   内存限制:512 MB [问题描述] Plants vs. Zombies(PVZ)是最近十分风靡的一款小游戏.Plants(植物)和Zombies(僵尸)是游戏的主角,其中Plants防守,而Zombies进攻.该款游戏包含多种不同的挑战系列,比如Protect Your Brain.Bowling等等.其中最为经典的,莫过于玩家通过控制Plants来

Bzoj1565 [NOI2009]植物大战僵尸

Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 2363  Solved: 1092 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

bzoj 1565 [NOI2009]植物大战僵尸

拓扑排序+最小割. 每个植物有它保护的一些植物,等价于选一些点的先决条件是其它一些点,最大权闭合子图问题. 发现图里有环,所以环里的点都不能选,先决条件是环里的点的点也不能选,所以把所有边反向拓扑排序,拓扑不到的点不能选. 无视掉不能选的点后裸最大权闭合子图建图就好了. 一个最大权闭合子图裸题的题解 1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm>

luogu2805 [NOI2009]植物大战僵尸

想象一下,要搞掉一个植物,必须先搞掉另一些植物--我们可以发现这是一个最大权闭合子图的问题. 最大权闭合子图的话,太空飞行计划问题是一个入门题,可以一看. 然而我们手玩一下样例就会惊恐地发现,保护关系出现了环. 比如说: 从 i 到 j 的箭头是说要搞 i 就要先搞 j,这是为了和最大权闭合子图的模型对应. 要搞 3 就要先搞 4,要搞 4 就要先搞 5,要搞 5 就要先搞 3--简直是乱来-- 这就像是一个无冷却食人花前面放一个坚果 所以就要删环了,把边倒过来拓扑排序一下,能被拓扑到的点就是合

NOI2009 植物大战僵尸

啊一道好题感觉写得挺爽的啊这题这种有一点懵逼然后学了一点东西之后很明朗的感觉真是好!预处理参考 :http://www.cppblog.com/MatoNo1/archive/2014/11/01/142798.html?opt=admin建图参考 :http://hihocoder.com/problemset/problem/1398?sid=1010444 #include<iostream> #include<cstdio> #include<cstdlib>

98植物大战僵尸OL_僵尸迷阵

最近在玩植物大战僵尸Ol打到僵尸迷阵,打了个700分倒数第一,擦.....俗话说的好,失败是成功的妈妈,于是花了点时间写了一个小玩意,我相信下次我一定能拿第一 代码非常简单: using UnityEngine; using System.Collections; using UnityEngine.UI; public class MyScript : MonoBehaviour { // Use this for initialization void Start () { } // Upd

植物大战僵尸2天空之城安卓版发布

全体注意!一大波飞行僵尸正在接近中——中国独创版<植物大战僵尸2天空之城>安卓版终于发布了.超乎想象的空战体验.全新僵尸植物军团.独特闪电环境效果和史无前例的战舰成长系统,让你和戴夫共同肩负守卫天空之城的荣耀重任.赶紧下载游戏,加入云端激战吧. <植物大战僵尸2天空之城>安桌版上线 [中国区独有版本 打造铿锵空战体验] <植物大战僵尸2>即将迎来两周年生日,为感谢中国玩家一直以来的热情支持,EA/PopCap携手拓维游戏打造了这款中国区独有新版本<植物大战僵尸2天