[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;
}






你可能感兴趣的:([BZOJ 1565][NOI 2009]植物大战僵尸(Dinic最大流+拓扑排序))