BZOJ 1565: [NOI2009]植物大战僵尸(最大权闭合子图+拓扑排序)(最大权闭合子图介绍)

题目

有一个N*M的方格,每个格子有一个权值,每个格子可以保护制定格子以及它后面的格子,只有当一个格子没有被保护的格子的时候才能被获取,求最大权值(原权值有正负)。

分析

最大权闭合子图的裸题。

然而最大权闭合子图是啥?

概念

闭合子图:选取一些结点使得这些结点所有出边都不会指向当前结点们之外的边。

最大权闭合子图:选取权值和最大的

它的应用就比如说大学选课,需要先修课,所有结点向自己的先修课连边,这样选取的最大权闭合子图就是最优值。

注意:最大权闭合子图可以不是一个连通块。

求法

建图
  • 首先建立原来的那个DAG图,如果不是DAG需要先拓扑排序去掉环(不能用tarjan)
  • 对于所有的边(u,v)全部在网络流中连边(u,v),流量为inf(inf至少为所有权值的和)
  • 对于所有正权结点,连接(s,u),权值为w;对于所有负权结点,连接(u,t),权值为-w(-w是个正数)
答案

ans=正权和-最小割(因为证明是用最小割证的)

和S连通的就是选择了的结点。

继续分析

那么就是个裸题了
注意由于两个点之间会被多次连边,所以最好用邻接矩阵
之前说过不能用tarjan,不过用了也只会错两个点。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=32*42,maxm=maxn*maxn,inf=1e9;
namespace IStream{  
    const int L=1<<15;  
    char buffer[L],*S,*T;  
    inline char Get_Char()  
    {  
        if(S==T)  
        {  
            T=(S=buffer)+fread(buffer,1,L,stdin);  
            if(S==T) return EOF;  
        }  
        return *S++;  
    }  
    inline void Rd(int &re) 
    {  
        char c; 
        re=0;
        int k=1;
        for(c=Get_Char();(c<'0'||c>'9') && c!='-';c=Get_Char());  
        if(c=='-')c=Get_Char(),k=-1;
        while(c>='0'&&c<='9')  
            re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();  
        re*=k;
    } 
}
namespace maxflow
{
    struct edge
    {
        int to,next,cap,flow;
    }E[maxm<<1];
    int sum;
    int np,first[maxn];
    void add(int u,int v,int c)
    {
        E[++np]=(edge){v,first[u],c,0};
        first[u]=np;
        E[++np]=(edge){u,first[v],0,0};
        first[v]=np;
    }
    int n,m,s,t;
    int dist[maxn],gap[maxn];
    void Initial()
    {
        np=-1;
        memset(first,-1,sizeof(first));
    }
    int SAP(int i,int lim)
    {
        if(i==t)return lim;
        int flow=0;
        for(int p=first[i];p!=-1;p=E[p].next)if(E[p].cap-E[p].flow)
        {
            int j=E[p].to;
            if(dist[j]+1==dist[i])
            {
                int tmp=SAP(j,min(lim-flow,E[p].cap-E[p].flow));
                flow+=tmp;
                E[p].flow+=tmp;
                E[p^1].flow-=tmp;
                if(flow==lim || dist[s]>=t)return flow;
            }
        }
        if(flow==0)
        {
            if(--gap[dist[i]]==0)dist[s]=t;
            gap[++dist[i]]++;
        }
        return flow;
    }
    void maxflow()
    {
        memset(gap,0,sizeof(gap));
        memset(dist,0,sizeof(dist));
        gap[0]=t;
        int flow=0;
        while(dist[s]<t)
            flow+=SAP(s,inf);
        printf("%d\n",sum-flow);
    }
}
namespace topo
{
    int np,first[maxn];
    bool g[maxn][maxn];
    int n,m,nm,w[maxn],rd[maxn];
    bool vis[maxn];
    void topo()
    {
        queue<int>q;
        for(int i=1;i<=nm;i++)if(!rd[i])q.push(i),vis[i]=1;
        while(!q.empty())
        {
            int i=q.front();q.pop();
            for(int j=1;j<=nm;j++)
            {
                if(!g[i][j])continue;
                rd[j]--;
                if(!rd[j])q.push(j),vis[j]=1;
            }
        }
    }
    #define getId(i,j) (i-1)*m+j
    void Init()
    {
        int id,id1,a,ni,nj;
        IStream::Rd(n);IStream::Rd(m);
        nm=n*m;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                id=id1=getId(i,j);
                IStream::Rd(w[id]);
                for(int k=j+1;k<=m;k++)
                {
                    id1++;
                    if(!g[id1][id])g[id1][id]=1,rd[id]++;
                }
                IStream::Rd(a);
                for(int k=1;k<=a;k++)
                {
                    IStream::Rd(ni);IStream::Rd(nj);
                    ni++,nj++;
                    for(int l=nj;l>=1;l--)
                        if(!g[id][getId(ni,l)])g[id][getId(ni,l)]=1,rd[getId(ni,l)]++;
                }
            }
        }
    }
    void Build()
    {
        maxflow::Initial();
        maxflow::s=nm+1;
        maxflow::t=maxflow::s+1;
        for(int i=1;i<=nm;i++)
        {
            for(int j=1;j<=nm;j++)
            {
                if(!g[i][j])continue;
                if(!vis[i] || !vis[j])continue;
                maxflow::add(j,i,inf);
            }
        }
        for(int i=1;i<=nm;i++)
        {
            if(!vis[i])continue;
            if(w[i]>0)maxflow::add(maxflow::s,i,w[i]),maxflow::sum+=w[i];
            if(w[i]<0)maxflow::add(i,maxflow::t,-w[i]);
        }
        maxflow::maxflow();
    }
    void work()
    {
        Init();
        topo();
        Build();
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    topo::work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值