[BZOJ 1565][NOI 2009]植物大战僵尸(Dinic最大流+拓扑排序)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1565

orz题目太神。。。膝盖已经跪烂。。。。

这题需要我们建立起植物和植物之间复杂的保护关系,以及吃植物得到的损失和回报,因此要用最大流搞,建模太神奇了,跪跪跪。。。

首先我们建立源点和汇点,对于每个植物,如果吃它可以得到能源,在源点和它之间连一条边,容量为得到的能源数量。如果吃它要消耗能源,则在它和汇点之间建立一条边,边权为消耗的能源个数。

然后对于每个植物,将它和它的保护区域中的每个植物分别连一条边,容量为无穷大。

然后要注意,每一行的最后一个点要和下一行的第一个点连一条边,容量为无穷大,这样才能保证做好的图是联通的。

考虑到有可能有一些植物们可以相互保护,这样的话僵尸是吃不了它们的,这种情况下在图中这些植物会连成一个环,因此接着要拓扑排序,把图中所有的环都消去。

最后Dinic跑最大流,答案=图上所有不属于环的植物的权值-最大流

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <queue>

#define MAXE 500500
#define MAXV 650
#define INF 0x3f3f3f3f

using namespace std;

int ans=0,f[MAXV],S,T,n,m;
bool used[MAXV]; //used[i]=true表明点i没有被去除掉
int inDegree[MAXV]; //每个点的入度

//Start Of Graph Structure
struct edge
{
    int u,v,cap,next;
}edges[MAXE];

int head[MAXV],nCount=-1;

void AddEdge(int U,int V,int C)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].cap=C;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

void add(int U,int V,int C)
{
    AddEdge(U,V,C);
    AddEdge(V,U,0);
    inDegree[U]++;
}
//End Of Graph Structure

//Start Of Topological Sort
void TopoSort()
{
    queue<int>q;
    while(!q.empty()) q.pop();
    for(int i=S;i<=T;i++)
        if(!inDegree[i])
            q.push(i);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        used[u]=true;
        if(f[u]>0)
            ans+=f[u];
        for(int p=head[u];p!=-1;p=edges[p].next)
        {
            int v=edges[p].v;
            if(p&1)
            {
                inDegree[v]--;
                if(!inDegree[v])
                    q.push(v);
            }
        }
    }
}
//End Of Topological Sort

//Start Of Dinic Max-flow Algorithm
int layer[MAXV]; //bfs分出的层

bool CountLayer()
{
    memset(layer,0,sizeof(layer));
    queue<int>q;
    while(!q.empty()) q.pop();
    q.push(S);
    layer[S]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int p=head[u];p!=-1;p=edges[p].next)
        {
            int v=edges[p].v;
            if(!layer[v]&&edges[p].cap&&used[v])
            {
                layer[v]=layer[u]+1;
                q.push(v);
                if(v==T) return true;
            }
        }
    }
    return false;
}

int DFS(int u,int flow)
{
    int tmp=flow;
    if(u==T) return flow;
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(edges[p].cap&&layer[v]==layer[u]+1&&tmp)
        {
            int now=DFS(v,min(tmp,edges[p].cap));
            if(!now) layer[v]=0;
            tmp-=now;
            edges[p].cap-=now;
            edges[p^1].cap+=now;
        }
    }
    return flow-tmp;
}

int Dinic()
{
    int maxflow=0;
    while(CountLayer())
        maxflow+=DFS(S,INF);
    return maxflow;
}
//End Of Dinic Max-flow Algorithm

int main()
{
    memset(head,-1,sizeof(head));
    int x,y,r,c;
    scanf("%d%d",&m,&n);
    S=0,T=n*m+1;
    for(int i=1;i<=m*n;i++)
    {
        scanf("%d",&f[i]);
        if(f[i]>0) //吃该植物能得到能量
            add(S,i,f[i]);
        else //吃该植物要付出能量
            add(i,T,-f[i]);
        scanf("%d",&y); //攻击范围
        for(int j=1;j<=y;j++)
        {
            scanf("%d%d",&r,&c);
            add(r*n+c+1,i,INF); //从被保护的植物到该植物连一条容量无穷大的边
        }
        if(i%n)
            add(i,i+1,INF);
    }
    TopoSort();
    cout << ans-Dinic() << endl;
    return 0;
}



时间: 2024-10-22 17:17:36

[BZOJ 1565][NOI 2009]植物大战僵尸(Dinic最大流+拓扑排序)的相关文章

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

植物大战僵尸 bzoj1565 题目大意:给你一张网格图,上面种着一些植物.你从网格的最右侧开始进攻.每个植物可以对僵尸提供能量或者消耗僵尸的能量.每个植物可以保护一个特定网格内的植物,如果一个植物被保护,那么如果僵尸想吃掉该植物就必须先吃掉保护它的植物.问:僵尸最多能获得多少能量. 注释:1<=N(网格的宽)<=20,1<=M(网格的长)<=30,-20,000<=代价和收益<=20,000. 想法:前置题目([NOI2006]最大获利).这道题和最大获利比较相像,如

BZOJ1565 NOI 2009 植物大战僵尸 topo+最小割(最大权闭合子图)

题目链接:https://www.luogu.org/problemnew/show/P2805(bzoj那个实在是有点小小的辣眼睛...我就把洛谷的丢出来吧...) 题意概述:给出一张有向图,这张有向图上的每个点都有一个点权,想要访问某个点必须要先访问这个点所能够访问(遍历)到的所有点,在访问到一个点之后将会得到这个点的权值(可正可负).问访问这张图可以得到的最大点权和. 原题说过来说过去实际上是描述了一个植物之间的保护关系,也就是说明了植物之间的先后访问顺序之间的关系.可以描述为要"要访问点

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

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1565 题意:植物大战僵尸,一个n*m的格子,每 个格子里有一个植物,每个植物有两个属性:(1)价值:(2)保护集合,也就是这个植物可以保护矩阵中的某些格子.现在你是僵尸,你每次只能从(i,m) 格子进入,从右向左进攻.若一个格子是被保护的那么你是不能进入的.每进入一个格子则吃掉该格子的植物并得到其价值(价值有可能是负的).注意,每次在进 入一行后还可以再退到最右侧然后再换一行吃别的.问

BZOJ 1565 NOI2009 植物大战僵尸 最大权闭合图+拓扑排序

题目大意:给定一个m*n的草坪,每块草坪上的植物有两个属性: 1.啃掉这个植物,获得收益x(可正可负) 2.保护(r,c)点的植物不被啃掉 任何一个点的植物存活时,它左侧的所有植物都无法被攻击 求最大收益 首先这个保护和被保护的关系就是最大权闭合图的连边关系 然后直接跑就行 然后我们就会发现没过样例0.0 原因当图出现环时,根据题意,环上的所有点都不能取(想象一个无冷却的食人花前面放一个坚果) 所以这题还要去掉环 由于环上的点不能取,所以所有指向环上的点的点都不能取 这个条件看起来不太好做,我们

【BZOJ-1565】植物大战僵尸 拓扑排序 + 最小割

1565: [NOI2009]植物大战僵尸 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 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

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

P1135 - 植物大战僵尸 From ytt    Normal (OI)总时限:10s    内存限制:128MB    代码长度限制:64KB 背景 Background 虽然这么多天了,,虽然寂寞的玩了好多遍植物大战僵尸,,还是觉得写题目比控制脑残僵尸比较好玩` 描述 Description 植物大战僵尸    [问题描述]Plants vs. Zombies(PVZ)是最近十分风靡的一款小游戏.Plants(植物)和Zombies(僵尸)是游戏的主角,其中Plants防守,而Zombi

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

b2OJ_1565_[NOI2009]植物大战僵尸_拓扑排序+最大权闭合子 题意:n*m个植物,每个植物有分数(可正可负),和能保护植物的位置.只能从右往左吃,并且不能吃正被保护着的,可以一个不吃,求获得的最大分数. 分析:把每个植物向能保护它的植物连边.源点连正权点,负权点连汇点. 考虑在一个环上的植物是吃不到的,我们可以用拓扑排序确定哪些是能吃的. 然后求一遍最大权闭合子图就是答案. 代码: 1 #include <stdio.h> 2 #include <string.h>

JAVA安卓植物大战僵尸主题四子棋游戏

@前言 这里使用安卓最基本的API实现双人四子棋游戏(无AI),开发语言为java,开发环境为Android Studio 2.1.2,目标SDK版本为24,最低为15: 界面采用植物大战僵尸主题,图片资源来源于网络,进行了PS加工,非原创: 游戏界面基本可以适配所有安卓手机分辨率,不过在分辨率太大或太小的手机上整体效果会有影响: Github源码: https://github.com/jiangxh1992/FourInRowGame 视频演示: https://vimeo.com/1875

BZOJ 2436 Noi嘉年华(优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2436 题意:有一些活动,起始时间持续时间已知.有两个场地.每个活动最多只能在一个场地举行,且两个场地同一时间不能都举行活动.但是同一场地同一时间可以举行多个活动.要求的是两个场地中活动数目少的场地的活动数目的最大值S.再输出某个活动必须被安排时的S值. 思路:我直接粘贴原思路了. 区间离散化,设A={嘉年华1的活动}, B={嘉年华2的活动},C={未安排的活动}. 设num[i][j