洛谷 P2774 方格取数问题 二者选一的问题

题目链接
首先说一下,这题和P1361 小M的作物 很像,同样的做题思路
题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。

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

输出格式
程序运行结束时,将取数的最大总和输出

输入输出样例
input
3 3
1 2 3
3 2 3
2 3 1
output
11

分析:
和[P1361 小M的作物]这题差不多,都是先需要在这个方格里面看是否有方格数之间存在某种联系,发现确实是存在的,根据题意,发现奇数点永远与偶数点是连接的,那么这样就可以将奇数点与偶数点相连接,可以设为INF,也可以设为S到奇数点与偶数点到T的和(当然,反过来也是可以的)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<cmath>
#include<cstring>
#include<set>
#include<queue>
#include<stack>
#include<map>
typedef long long ll;
using namespace std;
const int INF=0x3f3f3f3f;
struct node{
    int from,to,cap,flow;
};
vector<int>G[50000];
vector<node>edge;
void add(int from ,int to,ll cap){
    edge.push_back((node){from,to,cap,0});
    edge.push_back((node){to,from,0,0});
    int m=edge.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
}
int s,t;
bool vis[50000];
int d[50000],cur[50000];

bool bfs(){
    memset(vis,0,sizeof vis);
    queue<int>q;
    q.push(s);
    d[s]=0;
    vis[s]=1;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=0;i<G[x].size();i++){
            node& e=edge[G[x][i]];
            if(!vis[e.to] && e.cap>e.flow){
                vis[e.to]=1;
                d[e.to]=d[x]+1;
                q.push(e.to);
            }
        }
    }
    return vis[t];
}

int dfs(int x,int a){
    if(x==t|| a==0) return a;
    int flow=0,f;
    for(int & i=cur[x]; i< G[x].size(); i++){
        node & e = edge[G[x][i]];
        if(d[x]+1==d[e.to] && (f=(dfs(e.to,min(a,e.cap-e.flow))))>0){
            e.flow+= f;
            edge[G[x][i]^1].flow-=f;
            flow+=f;
            a-=f;
            if(a==0) break;
        }
    }
    return flow;
}

int dinic(){
    int flow=0;
    memset(d,0,sizeof d);
    while(bfs()){
        memset(cur,0,sizeof cur);
        flow+=dfs(s,INF);
    }
    return flow;
}
int e[220][220];
    int n,m;
bool check(int x,int y){
    if(x>n||y>m||x<1||y<1) return false;
    return true;
}
int dir[4][2]={{0,-1},{0,1},{1,0},{-1,0}};
int point(int x,int y){
   return (x-1) *m+y;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    scanf("%d%d",&n,&m);
    s=0;t=n*m+1;
    int sum=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&e[i][j]);
            sum+=e[i][j];
            if((i+j)&1)
            add(s,point(i,j),e[i][j]);
            else
                add(point(i,j),t,e[i][j]);
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if((i+j)&1){//这里就是奇数点的意思
                for(int k=0;k<4;k++){
                    int dx=i+dir[k][0],dy=j+dir[k][1];
                    if(check(dx,dy)){
                        add(point(i,j),point(dx,dy),e[i][j]+e[dx][dy]);//这里设成INF也可以
                    }
                }
            }
//            else{//下面这个else可不加,因为再建一条是不会影响到最小割的大小的,所以不会影响到最小割的值
//                for(int k=0;k<4;k++){
//                    int dx=i+dir[k][0],dy=j+dir[k][1];
//                    if(check(dx,dy)){
//                        add(point(dx,dy),point(i,j),e[i][j]+e[dx][dy]);
//                    }
//                }
//            }
        }
    }
    printf("%d\n",sum-dinic());
    return 0;
}

综上所述,遇见这种方格数目的匹配问题的时候,可以用最小割来去解决,分析一下奇数点和偶数点之间是否存在什么关系可以用于建图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值