[bzoj4657]tower

本文详细解析了BZOJ4657塔楼问题,通过将问题转化为最小割模型,利用图论算法进行求解。文章介绍了如何通过设置负数流量和INF限制来构建图,并采用S-T割算法实现最大流最小割定理,最终得出最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[bzoj4657]tower


建图真的学不来。。

首先我们考虑把题目转化成最小割。

然后这个题有些性质就是每个点最多有两种被选中的方案,然后我们想最小割的话就应该设成负数的流量(我们在这里强制加一个INF什么的就行了)。

然后关于只能选一个的限制我们参考切糕。

然后就有了建图,行列分别连S,T然后中间连INF的限制就行了。详见代码

  • 代码
#include<bits/stdc++.h>
using namespace std;
const int N=10010,M=2e6+5;
const int INF=0x3f3f3f3f;
int n,m;
namespace graph{
    int hed[N],cnt=1,to[M],nxt[M],cap[M],flow[M];
    int S,T;
    int d[N];bool vis[N];
    void adde(int u,int v,int _cap){
        ++cnt;to[cnt]=v,cap[cnt]=_cap,nxt[cnt]=hed[u],flow[cnt]=0;hed[u]=cnt;
        ++cnt;to[cnt]=u,cap[cnt]=0,nxt[cnt]=hed[v],flow[cnt]=0;hed[v]=cnt;
    }
    queue<int>Q;
    bool bfs(){
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        Q.push(S);vis[S]=1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=hed[u];i;i=nxt[i]){
                int v=to[i];
                if(!vis[v]&&cap[i]>flow[i]){
                    vis[v]=1,d[v]=d[u]+1;
                    Q.push(v);
                }
            }
        }
        return vis[T];
    }
    int dfs(int u,int F){
        if(u==T||!F)return F;
        int flownow=0;
        for(int i=hed[u];i;i=nxt[i]){
            int v=to[i];if(d[v]!=d[u]+1)continue;
            int f=dfs(v,min(F,cap[i]-flow[i]));
            flownow+=f;
            F-=f;
            flow[i]+=f;
            flow[i^1]-=f;
            if(!F)break;
        }
        if(!flownow)d[u]=0;
        return flownow;
    }
    int max_flow(){
        int fw=0;
        while(bfs()){
            fw+=dfs(S,INF);
        }
        return fw;
    }
}

int id(int x,int y,int w){
    return w*n*m+(x-1)*m+y;
}
int a[100][100];
const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
int ans=0;

int main(){
    using namespace graph;
    scanf("%d%d",&n,&m);
    S=0,T=n*m*2+1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            if(a[i][j]<0){
                int mx=0,x=i,y=j,t=-a[i][j]-1;
                while(1){
                    x+=dx[t],y+=dy[t];
                    if(x<1||x>n||y<1||y>m)break;
                    mx=max(mx,a[x][y]);
                }
                ans+=mx;
                if(t<2){
                    adde(S,id(i,j,0),INF);
                }else{
                    adde(id(i,j,1),T,INF);
                }
                x=i,y=j;
                while(1){
                    int xx=x,yy=y;
                    x+=dx[t],y+=dy[t];
                    if(x<1||x>n||y<1||y>m)break;
                    if(t<2){
                        adde(id(xx,yy,0),id(x,y,0),mx-max(0,a[xx][yy]));
                    }else{
                        adde(id(x,y,1),id(xx,yy,1),mx-max(0,a[xx][yy]));
                    }
                }
            }else{
                adde(id(i,j,0),id(i,j,1),INF);
            }
    }
    printf("%d\n",ans-max_flow());
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值