#网络流,最大流,dinic#洛谷 2774 ssl 2609 方格取数游戏

题目

在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。


分析

匈牙利算法做二分图匹配,是OK的,但是网络流更高效,按奇偶性建图,再建立源点和汇点,分别连接,跑一遍最大流。


代码

#include <cstdio>
#include <cctype>
#include <queue>
using namespace std;
struct node{int y,next,w;}e[60001];
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int k=1,n,m,ans,s,t,ls[10001],dis[10001];
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
void add(int x,int y,int w){
    e[++k].y=y; e[k].w=w; e[k].next=ls[x]; ls[x]=k;
    e[++k].y=x; e[k].w=0; e[k].next=ls[y]; ls[y]=k;
}
bool bfs(){
    for (int i=1;i<=n*m+2;i++) dis[i]=0;
    queue<int>q; q.push(s); dis[s]=1;
    while (q.size()){
        int x=q.front(); q.pop();
        for (int i=ls[x];i;i=e[i].next)
        if (e[i].w>0&&!dis[e[i].y]){
            dis[e[i].y]=dis[x]+1;
            if (e[i].y==t) return 1;
            q.push(e[i].y);
        }
    }
    return 0;
}
int min(int a,int b){return (a<b)?a:b;}
int dfs(int x,int now){
    if (x==t||!now) return now;
    int rest=0,f;
    for (int i=ls[x];i;i=e[i].next)
    if (e[i].w>0&&dis[e[i].y]==dis[x]+1){
        rest+=(f=dfs(e[i].y,min(now-rest,e[i].w)));
        e[i].w-=f; e[i^1].w+=f;
        if (now==rest) return rest;
    }
    if (!rest) dis[x]=0;
    return rest;
}
int main(){
    n=in(); m=in(); s=n*m+1,t=n*m+2;
    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++){
        int p=in(); ans+=p;
        if (!((i+j)&1)) add((i-1)*m+j,t,p);
        if ((i+j)&1){
            add(s,(i-1)*m+j,p);
            for (int o=0;o<4;o++){
                int x=i+dx[o],y=j+dy[o];
                if (x<1||x>n||y<1||y>m) continue;
                add((i-1)*m+j,(x-1)*m+y,1e8);
            }
        }
    }
    while (bfs()) ans-=dfs(s,1e9);
    return !printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/80772406
今日推荐