【BZOJ1565】【NOI2009】植物大战僵尸 网络流 最大权闭合子图

题目大意

​  给你一个\(n\times m\)的地图,每个格子上都有一颗植物,有的植物能保护其他植物。僵尸从右往左进攻,每吃掉一颗植物就可以得到\(a_{i,j}\)的收益(\(a_{i,j}\)可以是负数)。求僵尸的最大收益

​  \(1\leq n\leq20,1\leq m\leq30\)

题解

​  这种一个东西可以保护另一个东西而且数据范围那么小的题目显然是最大权闭合子图。

​  僵尸从右往左进攻,所以一棵植物可以保护它左边的植物。

​  如果保护关系形成一个环,就都不能选。

​  直接套模板即可。

​  可能需要一些优化

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
struct list
{
    int v[1000010];
    int w[1000010];
    int t[1000010];
    int h[1010];
    int n;
    list()
    {
        memset(h,0,sizeof h);
        n=0;
    }
    void add(int x,int y,int z)
    {
        n++;
        v[n]=y;
        w[n]=z;
        t[n]=h[x];
        h[x]=n;
    }
};
list l,l2;
void add(int x,int y,int z)
{
    l.add(x,y,z);
    l.add(y,x,0);
}
int n,m;
int w[110][110];
int gx[1010];
int gy[1010];
int id(int x,int y)
{
    return (x-1)*m+y;
}
int vis[1010];
int b[1010];
void dfs(int x)
{
    vis[x]=1;
    int i;
    for(i=l2.h[x];i;i=l2.t[i])
    {
        if(!vis[l2.v[i]])
            dfs(l2.v[i]);
        if(b[l2.v[i]]||vis[l2.v[i]]==1)
        {
//          w[gx[x]][gy[x]]=-10000000;
            b[x]=1;
        }
    }
    vis[x]=2;
}
int d[1010];
int S,T;
int c[30][40][30][40];
int bfs()
{
    memset(d,-1,sizeof d);
    queue<int> q;
    q.push(S);
    d[S]=0;
    int x,i;
    while(!q.empty())
    {
        x=q.front();
        q.pop();
        for(i=l.h[x];i;i=l.t[i])
            if(l.w[i]&&d[l.v[i]]==-1)
            {
                d[l.v[i]]=d[x]+1;
                if(l.v[i]==T)
                    return 1;
                q.push(l.v[i]);
            }
    }
    return 0;
}
int op(int x)
{
    return ((x-1)^1)+1;
}
int dfs(int x,int flow)
{
    if(x==T)
        return flow;
    int c,s=0,i;
    for(i=l.h[x];i;i=l.t[i])
        if(l.w[i]&&d[l.v[i]]==d[x]+1)
        {
            c=dfs(l.v[i],min(flow,l.w[i]));
            s+=c;
            flow-=c;
            l.w[i]-=c;
            l.w[op(i)]+=c;
            if(!flow)
                break;
        }
    if(flow)
        d[x]=-1;
    return s;
}
int main()
{
    memset(b,0,sizeof b);
    memset(c,0,sizeof c);
//  freopen("bzoj1565.in","r",stdin);
//  freopen("bzoj1565.out","w",stdout);
    scanf("%d%d",&n,&m);
    S=n*m+1;
    T=S+1;
    int sum=0;
    int i,j,k,o,x,y,u;
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
        {
            scanf("%d",&w[i][j]);
            gx[id(i,j)]=i;
            gy[id(i,j)]=j;
            scanf("%d",&u);
            for(k=1;k<=u;k++)
            {
                scanf("%d%d",&x,&y);
                x++;
                y++;
                c[i][j][x][y]=1;
            }
            if(j>1)
                c[i][j][i][j-1]=1;
            for(x=1;x<=n;x++)
                for(y=1;y<=m;y++)
                    if(c[i][j][x][y])
                        l2.add(id(x,y),id(i,j),0);
        }
    memset(vis,0,sizeof vis);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(!vis[id(i,j)])
                dfs(id(i,j));
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)   
            if(!b[id(i,j)])
            {
                if(w[i][j]>0)
                {
                    sum+=w[i][j];
                    add(S,id(i,j),w[i][j]);
                }
                else
                    add(id(i,j),T,-w[i][j]);
                for(x=1;x<=n;x++)
                    for(y=1;y<=m;y++)
                        if(c[i][j][x][y])
                            if(!b[id(x,y)])
                                add(id(x,y),id(i,j),0x7fffffff);
            }
    int ans=0;
    while(bfs())
        ans+=dfs(S,0x7fffffff);
    printf("%d\n",sum-ans);
    return 0;
}

转载于:https://www.cnblogs.com/ywwyww/p/8510677.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值