bzoj1565: [NOI2009]植物大战僵尸

传送门
这道题吃到某个植物a可能需要先吃掉别的植物b(在他的右边或者保护着他),那么我们把a连向b。
发现这是最大权闭合子图。
显然是可以通过网络流水过的。
闭合子图:
V中顶点的所有出边均指向V内部顶点
那么按照最大权闭合图的建图方法:
1.s向正权点连流量为权值的边
2.负权点向t连流量为权值的绝对值的边
3.有边相连的两点连流量为inf的边
答案就是正权点的权值总和减去最小割。
但是数据中可能会出现氪金植物(玩家)
他们之间的保护关系像一个环(想象一个无冷却的食人花前面放一个坚果)
显然非氪金玩家僵尸是干不掉他们的。
所以在跑网络流之前先进行拓扑排序。
强制删除不可达点即可。

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x3f3f3f3f
#define N 605
using namespace std;
struct edge{int to,f,next;}e[500005];
int head[N],in[N],dis[N],f[N],q[N],ok[N];
int n,m,S,T,p,x,y,ans,tot=1;
void ins(int x,int y,int w){
    e[++tot].to=y;
    e[tot].f=w;
    e[tot].next=head[x];
    head[x]=tot;
    e[++tot].to=x;
    e[tot].f=0;
    e[tot].next=head[y];
    head[y]=tot;
    in[x]++;
}
void topo(){
    int h=0,t=0,x;
    for (int i=1;i<=n*m+2;i++)
        if (!in[i]) q[++t]=i;
    while (h<t){
        x=q[++h];
        ok[x]=1;
        if (f[x]>0) ans+=f[x];
        for (int i=head[x];i;i=e[i].next)
            if (i&1){
                in[e[i].to]--;
                if (!in[e[i].to]) q[++t]=e[i].to;
            }
    }
}
bool bfs(){
    int h=0,t=1,x;
    memset(dis,-1,sizeof(dis));
    q[1]=S;
    dis[S]=0;
    while (h<t){
        x=q[++h];
        for (int i=head[x];i;i=e[i].next)
            if (dis[e[i].to]==-1&&e[i].f&&ok[e[i].to]){
                dis[e[i].to]=dis[x]+1;
                q[++t]=e[i].to;
            }
    }
    return dis[T]!=-1;
}
int dinic(int x,int flow){
    if (x==T) return flow;
    int rest=flow,k;
    for (int i=head[x];i&&rest;i=e[i].next)
        if (e[i].f&&dis[x]+1==dis[e[i].to]){
            k=dinic(e[i].to,min(rest,e[i].f));
            rest-=k;
            e[i].f-=k;
            e[i^1].f+=k;
        }
    if (rest) dis[x]=-1;
    return flow-rest;
}
int main(){
    scanf("%d%d",&n,&m);
    S=n*m+1;
    T=S+1;
    for (int i=1;i<=n*m;i++){
        scanf("%d",&f[i]);
        if (f[i]>0) ins(S,i,f[i]); else ins(i,T,-f[i]);
        scanf("%d",&p);
        for (int j=1;j<=p;j++){
            scanf("%d%d",&x,&y);
            ins(x*m+y+1,i,inf);
        }
        if (i%m) ins(i,i+1,inf);
    }
    topo();
    while (bfs()) ans-=dinic(S,inf);
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值