【网络流24题】方格取数(二分图染色+最大权独立点集+最小割)

8 篇文章 0 订阅
3 篇文章 0 订阅

传送门

    方格取数
    题意:给定权值棋盘,相邻棋子不能同时选择,求能够选出的棋子集合最大权。

I think

    对原图进行黑白染色,即将图分为x,y两个集合,增设源汇点S,T,S向x集合中连边,y集合中的点向T连边,边的容量均为点权,最后将x集合中的点向y中与之相邻的点连边,容量为Inf。答案最大权独立点集= 点权-最小割 。
    要明确删去所有割边之后,仍然与源/汇点相连的点权和即答案。

Code

#include<cstdio>
#include<vector>
#include<queue>
using namespace std;

const int sm = 905;
const int sn = 3400;
const int Inf = 0x3f3f3f3f;

int M,N,S,T,tot=1,Flw,Sum;
int to[sn],nxt[sn],hd[sm],c[sn];
int lev[sm],cur[sm];
int v[35][35],col[35][35];

int tr(int x,int y) { return (x-1)*N+y; }

int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v,int w) {
    to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=w;
    to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,c[tot]=0;
}
bool Bfs() {
    queue<int>q; int t;
    for(int i=1;i<=T;++i) lev[i]=0;
    lev[S]=1,q.push(S);
    while(!q.empty()) {
        t=q.front(),q.pop();
        for(int i=hd[t];i;i=nxt[i])
            if(c[i]>0&&!lev[to[i]]) {
                lev[to[i]]=lev[t]+1;
                if(to[i]==T) return 1;
                q.push(to[i]);
            }
    }
    return 0;
}
int Dfs(int x,int mx) {
    if(x==T||!mx) return mx;
    int f;
    for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
        cur[x]=i;
        if(c[i]>0&&lev[to[i]]==lev[x]+1)
            if(f=Dfs(to[i],Min(c[i],mx)))
                return c[i]-=f,c[i^1]+=f,f;
    }
    return 0;
}
void Dinic() {
    int f;
    while(Bfs()) {
        for(int i=1;i<=T;++i) cur[i]=0;
        while(f=Dfs(S,Inf)) Flw+=f;
    }
} 
int main() {
    int p,q;col[1][1]=1;
    scanf("%d%d",&M,&N);
    S=M*N+1,T=S+1;
    for(int i=1;i<=M;++i)
        for(int j=1;j<=N;++j) {
            scanf("%d",&v[i][j]);
            Sum+=v[i][j];
            p=(col[i][j]==1)?2:1;
            if(i+1<=M) col[i+1][j]=p;
            if(j+1<=N) col[i][j+1]=p;
        }
    for(int i=1;i<=M;++i)
        for(int j=1;j<=N;++j) {
            q=tr(i,j);
            if(col[i][j]==1) {
                Add(S,q,v[i][j]);
                if(i-1>=1) Add(q,tr(i-1,j),Inf);
                if(i+1<=M) Add(q,tr(i+1,j),Inf);
                if(j-1>=1) Add(q,tr(i,j-1),Inf);
                if(j+1<=N) Add(q,tr(i,j+1),Inf);
            }
            else Add(q,T,v[i][j]);
        }
    Dinic();
    printf("%d\n",Sum-Flw);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值