網路流24題09方格取數問題

wondover發表於2018-03-01

網路流24題09方格取數問題

題目描述

在一個有 m*n 個方格的棋盤中,每個方格中有一個正整數。現要從方格中取數,使任意 2 個數所在方格沒有公共邊,且取出的數的總和最大。試設計一個滿足要求的取數演算法。對於給定的方格棋盤,按照取數要求程式設計找出總和最大的數。

輸入

第 1 行有 2 個正整數 m 和 n,分別表示棋盤的行數和列數。接下來的 m 行,每行有 n 個正整數,表示棋盤方格中的數。

輸出

程式執行結束時,將取數的最大總和輸出


Solution

考慮網路流。
把每個可以同時選的兩個點建邊,最後要使流量最大
可是n<=100

n<=100
,邊的數量就會爆炸
不如換種方式考慮。
假設先把全部的選上,然後把不滿足的刪掉,要使刪掉的數量儘可能小。
這樣就有了一種最小割的感覺。
將所有點按照所在行和列之和的奇偶性分成兩部分(因為與一個點相鄰的點奇偶性不同,就形成了一張二分圖)
建圖:
1. 將S與每個奇點相連,權值為這個點的價值(意思是將這個點刪除要花費w的代價)
2. 同理將每個偶點與T相連,權值為這個點的價值
3. 從每個奇點向相鄰的每個偶點連一條權值為inf的邊,說明這兩個點必然至少有一個會被刪掉。
然後跑一邊最小割。
因為最小割求的是要使刪的數目最小,所以答案是總和-最小割。
洛谷上可以測

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define maxn 1000010
const int inf=1e9+7;
inline int read(){
    int ret=0,ff=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') ff=-ff;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        ret=ret*10+ch-'0';
        ch=getchar();
    }
    return ret*ff;
}
int n,m,s,t;
struct Edge{
    int u,v,w,next;
}E[maxn];
int ecnt=1;
int head[maxn],dis[maxn];
void addedge(int u,int v,int w){
    E[++ecnt].u=u;
    E[ecnt].v=v;
    E[ecnt].w=w;
    E[ecnt].next=head[u];
    head[u]=ecnt;
}
void Addedge(int u,int v,int w){
    addedge(u,v,w);
    addedge(v,u,0);
}
int dfs(int x,int nar){
    if(x==t) return nar;
    int used=0;
    for(int i=head[x];i;i=E[i].next){
        int v=E[i].v;
        if(dis[v]!=dis[x]+1||!E[i].w) continue;
        int tmp=nar-used;
        int flow=dfs(v,min(tmp,E[i].w));
        used+=flow;
        E[i].w-=flow;
        E[i^1].w+=flow;
        if(used==nar) return nar;
    }
    if(!used) dis[x]=-1;
    return used;
}
bool bfs(){
    queue<int> q;
    for(int i=1;i<=t;++i) dis[i]=inf;
    dis[s]=1;
    q.push(s);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=E[i].next){
            int v=E[i].v;
            if(dis[v]<inf||!E[i].w) continue;
            q.push(v);
            dis[v]=dis[x]+1;
        }
    }
    return dis[t]!=inf;
}
int Dinic(){
    int ans=0;
    while(bfs()){
        ans+=dfs(s,inf);
    }
    return ans;
}
int main(){
    n=read(),m=read();
    s=n*m+1,t=s+1;
    int sum=0;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            int c=read();
            sum+=c;
            int p=(i-1)*m+j;
            if((i+j)&1){
                Addedge(s,p,c);
                if(i>1) Addedge(p,p-m,inf);
                if(i<n) Addedge(p,p+m,inf);
                if(j>1) Addedge(p,p-1,inf);
                if(j<m) Addedge(p,p+1,inf);
            }
            else Addedge(p,t,c);
        }
    }
    printf("%d\n",sum-Dinic());
    return 0;
}

相關文章