• [网络流24题]方格取数问题


    题目:洛谷P2774、codevs1907。

    题目大意:在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。

    解题思路:本题关键在建模。

    把棋盘黑白染色(像国际象棋棋盘那样)。

    对于黑色的,连接源点,容量为格子中的数。

    对于白色的,连接汇点,容量为格子中的数。(黑白可以互换)

    然后把所有黑点和相邻的白点连边,容量无穷大。

    于是我们构造出了一个二分图。

    题目说不能取相邻的格子,那么对于这张二分图来说,就不能让他有一条边有流经过(否则就取了两个相邻的格子了)。

    于是求最小割即可。=求最大流

    最后用总和减去最小割即可

    C++ Code:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define inf 0x3fffffff
    std::queue<int>q;
    int n,m,cnt=-1,head[20005],iter[20005],level[20005];
    struct edge{
        int to,cap,nxt;
    }e[10005*8];
    inline int number(int x,int y){
        return(x-1)*m+y;
    }
    inline void addedge(int from,int to,int flow){
        ++cnt;
        e[cnt]=(edge){to,flow,head[from]};
        head[from]=cnt;
        ++cnt;
        e[cnt]=(edge){from,0,head[to]};
        head[to]=cnt;
    }
    void bfs(int s){
        level[s]=1;
        q.push(s);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=head[u];i!=-1;i=e[i].nxt)
            if(e[i].cap&&level[e[i].to]==-1){
                level[e[i].to]=level[u]+1;
                q.push(e[i].to);
            }
        }
    }
    int dfs(int u,int t,int f){
        if(u==t)return f;
        for(int& i=iter[u];i!=-1;i=e[i].nxt)
        if(e[i].cap&&level[u]<level[e[i].to]){
            int d=dfs(e[i].to,t,e[i].cap>f?f:e[i].cap);
            if(d){
                e[i].cap-=d;
                e[i^1].cap+=d;
                return d;
            }
        }
        return 0;
    }
    int maxflow(int s,int t){
        for(int flow=0;;){
            memset(level,-1,sizeof level);
            bfs(s);
            if(level[t]==-1)return flow;
            memcpy(iter,head,sizeof iter);
            int f;
            while(f=dfs(s,t,inf))flow+=f;
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        int sum=0;
        memset(head,-1,sizeof head);
        for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j){
            int t;
            scanf("%d",&t);
            sum+=t;
            int p=number(i,j);
            if((i+j)&1){
                addedge(0,p,t);
                if(i-1)addedge(p,number(i-1,j),inf);
                if(j-1)addedge(p,number(i,j-1),inf);
                if(i+1<=n)addedge(p,number(i+1,j),inf);
                if(j+1<=m)addedge(p,number(i,j+1),inf);
            }else addedge(p,n*m+1,t);
        }
        return!printf("%d
    ",sum-maxflow(0,n*m+1));
    }
  • 相关阅读:
    java学习大方向
    js总结001
    MySQL总结
    快捷键
    java基础总结001
    网络设备的发展与作用(冲突域与广播域)
    二进制&八进制&十六进制之间的快速转换------ 心算&笔算方法总结
    IP地址的定义和划分
    初学Linux基本的命令操作应当记牢
    JDK的下载与安装
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/8298605.html
Copyright © 2020-2023  润新知