lkb 的小屋

start again. //小蒟蒻也有大梦想

[SMOJ2207]方格取数问题

这题的思想跟“太空飞行计划”很类似。

首先把棋盘黑白染色,使所有相邻格子的颜色相反,此时需要改变我们之前把“合法的东西连边”的想法,变成“有冲突的连边”。也就是说,从每个黑色格子向其四周会与其冲突的白色格子之间连边(边界的特殊情况除外)。这样就得到了一个带点权的二分图。同样,别忘了源点和汇点。样例如图:

这样,我们的任务就变成了从二分图中选取一些点,使得被选择的任意两点间没有边相连。这就是二分图最大点权独立集问题。

带点权的网络流问题我们暂时不会解决,但可以转化成边权。令 s 到黑点的边容量为对应格子上的数,黑白点之间边容量为无穷大,白色点到 t 的边容量为对应格子上的数。考虑到取数的总和=方格中所有数之和-放弃的数之和。方格中所有格子数之和是一定的,要使取数的总和最大,就要使放弃的数之和尽可能小。

我们知道,有边相连的黑白格子之间一定要至少放弃一个。如果选其中的黑格子,就要割掉白格子到 t 的边,割了之后放弃的就是白格子上的数,也就是白格子到 t 的边权;反之亦然。最终要通过割一些边使 st 不连通,而所割之和即为放弃的数之和。故所要求的其实就是最小割,可以直接跑最大流,再用所有数字之和减去,就得到了答案。

参考代码:

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;

const int MAXM = 100;
const int MAXN = 100;
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
const int INF = 0x3f3f3f3f;

struct Edge {
    Edge *next;
    int cap;
    int dest;
} edges[MAXM * MAXN << 1], *current, *first_edge[MAXM * MAXN];

int m, n, s, t;
bool vis[MAXM * MAXN];

inline int pos(int x, int y) { return x * n + y; } //根据行列分配唯一的顶点编号

Edge *counterpart(Edge *x) {
    return edges + ((x - edges) ^ 1);
}

void insert(int u, int v, int c) {
    current -> next = first_edge[u];
    current -> cap = c;
    current -> dest = v;
    first_edge[u] = current ++;
}

int dfs(int u, int f) {
    if (u == t) return f;
    if (vis[u]) return 0; else vis[u] = true;
    for (Edge *p = first_edge[u]; p; p = p -> next)
        if (p -> cap)
            if (int res = dfs(p -> dest, min(f, p -> cap))) {
                p -> cap -= res;
                counterpart(p) -> cap += res;
                return res;
            }
    return 0;
}

int main(void) {
    freopen("2207.in", "r", stdin);
    freopen("2207.out", "w", stdout);
    scanf("%d%d", &m, &n); current = edges;
    s = 0; t = m * n + 1; int ans = 0;
    fill(first_edge, first_edge + t + 1, (Edge*)0);
    for (int i = 0; i < m; i++)
        for (int j = 1; j <= n; j++) {
            int num; scanf("%d", &num); ans += num;
            if ((i + j) & 1) { //黑
                insert(s, pos(i, j), num); insert(pos(i, j), s, 0);
                for (int k = 0; k < 4; k++) {
                    int nx = i + dx[k], ny = j + dy[k];
                    if (nx >= 0 && nx < m && ny > 0 && ny <= n) insert(pos(i, j), pos(nx, ny), INF), insert(pos(nx, ny), pos(i, j), 0);
                }
            } else insert(pos(i, j), t, num), insert(t, pos(i, j), 0); //白
        }
    while (true) {
        memset(vis, false, sizeof vis);
        if (int res = dfs(s, INF)) ans -= res; else break;
    }
    printf("%d\n", ans);
    return 0;
}


阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013686535/article/details/77152103
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

[SMOJ2207]方格取数问题

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭