P2774 方格取数问题

\(\color{#0066ff}{题目描述}\)

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

\(\color{#0066ff}{输入格式}\)

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

\(\color{#0066ff}{输出格式}\)

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

\(\color{#0066ff}{输入样例}\)

3 3
1 2 3
3 2 3
2 3 1 

\(\color{#0066ff}{输出样例}\)

11

\(\color{#0066ff}{数据范围与提示}\)

m,n<=100

\(\color{#0066ff}{题解}\)

最小割 = 最大流

通过把每个点横纵坐标相加的奇偶性,分为奇点和偶点

可以发现,奇点之间互不影响,偶点同理

S向所有奇点连边,所有偶点向T连边

奇点向相邻偶点连边

跑最大流(最小割)

最后用总数减就行了

#include <bits/stdc++.h>
#define LL long long
inline LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    return x * f;
}
const int max = 105050;
const int inf = 0x7fffffff;
struct node {
    int to, dis;
    node *nxt, *lst;
    node(int to = 0, int dis = 0, node *nxt = NULL):to(to), dis(dis), nxt(nxt) {}
    void *operator new (size_t) {
        static node *S = NULL, *T = NULL;
        return (S == T) && (T = (S = new node[1024]) + 1024), S++;
    }
};
int rx[] = {0, 0, -1, 1};
int ry[] = {1, -1, 0, 0};
typedef node* nod;
nod head[max], cur[max];
int dep[max];
int id[120][120], mp[120][120];
std::queue<int> q;
int n, m, s, t;
int ans;
inline void add(int from, int to, int dis) {
    nod o = new node(to, dis, head[from]);
    head[from] = o;
}
inline void link(int from, int to, int dis) {
    add(from, to, dis);
    add(to, from, 0);
    head[from]->lst = head[to];
    head[to]->lst = head[from];
}
inline bool bfs() {
    for(int i = s; i <= t; i++) dep[i] = 0, cur[i] = head[i];
    dep[s] = 1;
    q.push(s);
    while(!q.empty()) {
        int tp = q.front(); q.pop();
        for(nod i = head[tp]; i; i = i->nxt) {
            if(!dep[i->to] && i->dis > 0 ) {
                dep[i->to] = dep[tp] + 1;
                q.push(i->to);
            }
        }
    }
    return dep[t];
}
inline int dfs(int x, int change) {
    if(x == t || !change) return change;
    int flow = 0, ls;
    for(nod i = head[x]; i; i = i->nxt) {
        cur[x] = i;
        if(dep[i->to] == dep[x] + 1 && (ls = dfs(i->to, std::min(change, i->dis)))) {
            flow += ls;
            change -= ls;
            i->dis -= ls;
            i->lst->dis += ls;
            if(!change) break;
        }
    }
    return flow;
}
inline int dinic() {
    int flow = 0;
    while(bfs()) flow += dfs(s, inf);
    return flow;
}
int main() {
    n = in(), m = in();
    for(int cnt = 0, i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            id[i][j] = ++cnt, ans += (mp[i][j] = in());
    s = 0, t = id[n][m] + 1;
    for(int cnt = 0, i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) {
            if((i + j) & 1) {
                link(s, id[i][j], mp[i][j]);
                for(int k = 0; k < 4; k++) {
                    int xx = i + rx[k];
                    int yy = j + ry[k];
                    if(xx >= 1 && xx <= n && yy >= 1 && yy <= m)
                        link(id[i][j], id[xx][yy], inf);
                }
            }
            else link(id[i][j], t, mp[i][j]);
        }
    printf("%d", ans - dinic());
    return 0;
}

原文地址:https://www.cnblogs.com/olinr/p/10127108.html

时间: 2024-07-29 10:20:57

P2774 方格取数问题的相关文章

P2774 方格取数问题 网络最大流 割

P2774 方格取数问题:https://www.luogu.org/problemnew/show/P2774 题意: 给定一个矩阵,取出不相邻的数字,使得数字的和最大. 思路: 可以把方格分成两个部分,横坐标和纵坐标和为奇数的一组,和为偶数的一组,超级源点向偶数一组连容量为格点数字大小的边,奇数一组向超级汇点连容量为格点大小的边.然后两组间相临的点连容量为无穷的边. 跑出这个图的最大流,相当于是最小割,就是去掉了最少的部分使得网络不流通.因此答案就是sum - dinic(): #inclu

P2774 方格取数问题 网络流

题目: P2774 方格取数问题 题目背景 none! 题目描述 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法.对于给定的方格棋盘,按照取数要求编程找出总和最大的数. 输入输出格式 输入格式: 第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数.接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数. 输出格式: 程序运行结束时,将取数的最大总和输出 输入输出样

P2774 方格取数问题 网络流重温

P2774 方格取数问题 这个题目之前写过一次,现在重温还是感觉有点难,可能之前没有理解透彻. 这个题目要求取一定数量的数,并且这些数在方格里面不能相邻,问取完数之后和最大是多少. 这个很好的用了网络流的最大独立集. 根据位置把这些数分成了两个独立集,两个独立集的意思是这两个集合之间有关系,但是集合内部没有任何关系, 所以是两个独立集. 分成独立集之后,我们就要建图连边,这些都很好做,但是为什么答案就是  所有数之和-最小割 因为当我们跑一次最小割之和是不是让这个图没有连接了,也就是这个图不是联

P2774 方格取数问题(最小割)

P2774 方格取数问题 一看题目便知是网络流,但由于无法建图.... 题目直说禁止那些条件,这导致我们直接建图做不到,既然如此,我们这是就要逆向思维,他禁止那些边,我们就连那些边. 我们将棋盘染色,一个点向四周连边,我们的目标是使的这些边不起作用,我们将黑点与s联通,白点与t联通. 之后我们就要考虑一个事情,只要一个黑点与白点由流,此时一定s到t有流.这样我们就能想到最小割... #include<bits/stdc++.h> #define ll long long using names

洛谷 P2774 方格取数问题

题目背景 none! 题目描述 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法.对于给定的方格棋盘,按照取数要求编程找出总和最大的数. 输入输出格式 输入格式: 第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数.接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数. 输出格式: 程序运行结束时,将取数的最大总和输出 输入输出样例 输入样例#1: 3 3 1 2

[洛谷P2774] 方格取数问题

题意 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大. 想法 我们将问题转化为:一开始所有格子都选,之后去掉价值和最少的一些格子使剩下的格子合法. 将方格黑白染色,白格子连向S,黑格子连向T,边权为这个格子的值(也就是说将格子转移到边上). 相邻的黑白格子间连INF的边.(这样每条从S到T的路径都为 S->白格子->黑格子->T , 这两个格子不能同时选,S->白格子 与 黑格子->T 间必

luogu P2774 方格取数问题

有限制的问题,显然考虑全选再根据限制去掉的想法较优,我们发现一个点四周的点受限,其x或者y差一,也就是说奇偶性不同,那我们可以将其分成白点和黑点,就变成了最小割的问题,将每个白点向受限制的黑点连边,capacity为INF,每个黑点向汇点连边,capacity为该点的值,同理,源点向每个白点连边,这样受限的每一组之间都只会选出一个最小的来,通过capacity的限制来实现,最大流=最小割,将总和减去最小割(每一组最小的)就是答案 每一组黑白点,capacity来限制最小权,转换求最小割 #inc

hdu 1565 方格取数(2)(网络流之最大点权独立集)

题目链接:hdu 1565 方格取数(2) 题意: 有一个n*m的方格,每个方格有一个数,现在让你选一些数.使得和最大. 选的数不能有相邻的. 题解: 我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有: 最大点权独立集 + 最小点权覆盖集 = 总点权和, 这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图, 1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一

P1004 方格取数

P1004 方格取数 题目描述 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 人数字0.如下图所示(见样例): A 0 0 0 0 0 0 0 0 0 0 13 0 0 6 0 0 0 0 0 0 7 0 0 0 0 0 0 14 0 0 0 0 0 21 0 0 0 4 0 0 0 0 15 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . B 某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角