• 【codevs1907】【方格取数3】二分图最大带权独立集


    这里写图片描述
    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=59001242
    向大(hei)佬(e)势力学(di)习(tou)

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

    输入描述 Input Description
    第1 行有2 个正整数m和n,分别表示棋盘的行数
    和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。

    输出描述 Output Description
    将取数的最大总和输出

    样例输入 Sample Input
    3 3
    1 2 3
    3 2 3
    2 3 1

    样例输出 Sample Output
    11

    数据范围及提示 Data Size & Hint
    n,m<=30

    最近学图论,精神大餐吃的太饱不消化,就多攻克一下网络流和二分图吧。

    这道题其实是最小割的应用,兼有二分图的思想在里面。想想,要使选出来的最大,就要使留下来的最小,而我们刚好有求最小的东西(最大流=最小割)
    方格是天然的二分图,可以将方格染成交叉的黑白图,黑的丢一边,白的丢一边,相邻的连边,容量正无穷,表示不能割掉。源点与黑的连边,容量即为格子中的数,白点与汇点连边,同理。割掉的边即为舍弃的格子。

    代码(注意点的编号不能重复)

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    using namespace std;
    
    const int oo=0x3f3f3f3f;
    
    int m,n,a[50][50],sz=0,top;
    struct Node{
        int to,nxt,flow,cap;
    }r[6000];
    int head[3000],hh=1,dep[3000];
    queue<int> q;
    
    void adde(int fr,int to,int val){
        hh++;
        r[hh].to=to;
        r[hh].flow=0;
        r[hh].cap=val;
        r[hh].nxt=head[fr];
        head[fr]=hh;
        hh++;
        r[hh].to=fr;
        r[hh].flow=0;
        r[hh].cap=0;
        r[hh].nxt=head[to];
        head[to]=hh;
    }
    bool bfs(){
        while(!q.empty()) q.pop();
        memset(dep,-1,sizeof(dep));
        q.push(0);
        dep[0]=0;
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=head[u];i;i=r[i].nxt){
                int v=r[i].to;
                if(dep[v]==-1&&r[i].cap-r[i].flow>0){
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
            if(u==top) return true;
        }
        return false;
    }
    int dfs(int u,int delta){
        if(u==top||delta==0) return delta;
        int rt=0,f;
        for(int i=head[u];i;i=r[i].nxt){
            int v=r[i].to;
            if(dep[v]==dep[u]+1&&r[i].cap-r[i].flow>0){
                f=dfs(v,min(delta,r[i].cap-r[i].flow));
                rt+=f;
                delta-=f;
                r[i].flow+=f;
                r[i^1].flow-=f;
            }
            if(delta==0) break;
        }
        return rt;
    }
    int main(){
        top=2500;
        int tot=0;
        scanf("%d%d",&m,&n);
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++){
                scanf("%d",&a[i][j]);
                tot+=a[i][j];
                if((i+j)%2==0){
                    adde(0,i*50+j,a[i][j]);
                    if(i-1>0) adde(i*50+j,(i-1)*50+j,oo);
                    if(i+1<=m) adde(i*50+j,(i+1)*50+j,oo);
                    if(j-1>0) adde(i*50+j,i*50+j-1,oo);
                    if(j+1<=n) adde(i*50+j,i*50+j+1,oo);
                }
                if((i+j)%2!=0){
                    adde(i*50+j,top,a[i][j]);
                }
            }
        int ans=0;
        while(bfs()){
            ans+=dfs(0,oo);
        }
        printf("%d",tot-ans);
        return 0;
    }

    总结:
    1、遇到求最值问题,可从最大、最小两个方向出发。
    2、转化、建边也是很需要思维的东西
    3、天然的二分图:方格。当一个东西有两个元素时,可考虑二分图或网络流(源点汇点各一方嘛)

  • 相关阅读:
    转载:通过Servlet生成验证码
    转载:web工程中URL地址的推荐写法
    使用Git上传本地项目代码到github
    $watch 和 $apply
    平时用的sublime插件
    zTree.js
    js
    npm install详解
    git
    git基础
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763157.html
Copyright © 2020-2023  润新知