HDU-3338 Kakuro Extension(最大流,方格横纵和问题)

题目链接:HDU-3338 Kakuro Extension

题意

给出一个$n\times m$的网格,每个格子为黑色或白色,对于一行中连续的若干个白色格子,我们要往这若干个白色格子中填入$1\sim 9$的数字,使其和等于左边黑色格子中的一个已知数字$a_1$;对于一列中连续的若干个白色格子,同理填入$1\sim 9$的数字使其和等于上边黑色格子中的一个已知数字$a_2$。如果一个黑色格子相邻的右边和下边都有白色格子,那么这个黑色格子是带有两个已知数字$a_1$和$a_2$的,分别代表右边和下边白色格子的和。要求给出所有白色格子的填数方案。


思路

抽象出源点$s$和汇点$t$,下面用(始点,终点,容量)来表示流网络中的一条边,建图:

对于带有$a_1$的黑色格子$u$,连边$(s, u, a_1-cnt_1)$($cnt_1$表示$u$右边相邻连续的白色格子数量);

对于带有$a_2$的黑色格子$u‘$,连边$(u‘,t,a_2-cnt_2)$($cnt_2$表示$u$下边相邻连续的白色格子数量);

对于带有$a_1$和$a_2$的黑色格子,则需要拆成两个点$u$和$u‘$,连边$(s, u, a_1-cnt_1)$,$(u‘,t,a_2-cnt_2)$;

对于白色格子$v$的连边,由于填入的数字有$1\sim 9$的限制,是有上下界限制的网络流,我们用边$(v,u‘)$的流量来表示填入的数字,因为最大流中有的边流量可能为$0$,所以我们将$1\sim 9$的限制改为$0\sim 8$,最后输出的时候再加$1$即可(这也是为什么上面的连边都要减掉$cnt_1$或$cnt_2$,因为每个格子数字减了$1$,总和就要减掉格子数量),这样我们应该连的边为$(u, v, 8), (v, u‘, 8)$。

Ford-Fulkerson方法跑最大流后,边$(v,u‘)$的初始容量$8$减去残余容量就是这条边的流量,再$+1$就是$v$对应的白色格子应填入的数字。

实际实现中,用合理的编号表示上面所提到的$u,u‘,v$等结点,不要不同结点用相同编号即可。


代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using std::queue;
const int INF = 0x3f3f3f3f, N = 22000, M = 55000;
int head[N], d[N];
int s, t, tot, maxflow;
struct Edge
{
    int to, cap, nex;
} edge[M];
struct Grid
{
    int x, y;
    bool color;
} grid[110][110];

queue<int> q;
void add(int x, int y, int z) {
    edge[++tot].to = y, edge[tot].cap = z, edge[tot].nex = head[x], head[x] = tot;
    edge[++tot].to = x, edge[tot].cap = 0, edge[tot].nex = head[y], head[y] = tot;
}
bool bfs() {
    memset(d, 0, sizeof(d));
    while (q.size()) q.pop();
    q.push(s); d[s] = 1;
    while (q.size()) {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = edge[i].nex) {
            int v = edge[i].to;
            if (edge[i].cap && !d[v]) {
                q.push(v);
                d[v] = d[x] + 1;
                if (v == t) return true;
            }
        }
    }
    return false;
}
int dinic(int x, int flow) {
    if (x == t) return flow;
    int rest = flow, k;
    for (int i = head[x]; i && rest; i = edge[i].nex) {
        int v = edge[i].to;
        if (edge[i].cap && d[v] == d[x] + 1) {
            k = dinic(v, std::min(rest, edge[i].cap));
            if (!k) d[v] = 0;
            edge[i].cap -= k;
            edge[i^1].cap += k;
            rest -= k;
        }
    }
    return flow - rest;
}
void init(int n) {
    tot = 1, maxflow = 0;
    s = n + 1, t = s + 1;
    memset(head, 0, sizeof(head));
}
void read_build(int n, int m) {
    char str[10];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            scanf(" %s", str);
            if (str[0] == ‘.‘) {
                grid[i][j].color = grid[i][j].x = grid[i][j].y = 0;
                continue;
            }
            grid[i][j].color = 1;
            if (str[0] != ‘X‘) grid[i][j].y = (str[0] - ‘0‘) * 100 + (str[1] - ‘0‘) * 10 + (str[2] - ‘0‘);
            else grid[i][j].y = 0;
            if (str[4] != ‘X‘) grid[i][j].x = (str[4] - ‘0‘) * 100 + (str[5] - ‘0‘) * 10 + (str[6] - ‘0‘);
            else grid[i][j].x = 0;
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            int cnt = 0;
            if (grid[i][j].color && grid[i][j].y) {
                for (int k = i + 1; k < n; k++) {
                    if (grid[k][j].color) break;
                    ++cnt;
                    add(k * m + j, n * m + i * m + j, 8);
                }
                add(n * m + i * m + j, t, grid[i][j].y - cnt);
            }
            cnt = 0;
            if (grid[i][j].color && grid[i][j].x) {
                for (int k = j + 1; k < m; k++) {
                    if (grid[i][k].color) break;
                    ++cnt;
                    add(i * m + j, i * m + k, 8);
                }
                add(s, i * m + j, grid[i][j].x - cnt);
            }
        }
    }
}
void print(int n, int m) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j].color == 1) {
                printf("_%c", " \n"[j==m-1]);
                continue;
            }
            int res = 0;
            for (int k = head[i*m+j]; k; k = edge[k].nex) {
                if (k % 2 == 0) res += edge[k].cap;
            }
            printf("%d%c", 9 - res, " \n"[j==m-1]);
        }
    }
}

int main() {
    int n, m;
    while (~scanf("%d %d", &n, &m)) {
        init(2 * n * m);
        read_build(n, m);
        while (bfs()) maxflow += dinic(s, INF);
        print(n, m);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/kangkang-/p/11334952.html

时间: 2024-11-13 06:53:23

HDU-3338 Kakuro Extension(最大流,方格横纵和问题)的相关文章

HDU - 3338 Kakuro Extension(最大流)

题目大意:看一下图基本就知道了 解题思路:难点是构图.. 设置一个超级源点和所有的行的和相连,容量为该行的和 - 该行和由几个数相加得到 设置一个超级汇点,和所有列的和相连,容量为该列的和 - 该列和由几个数相加得到 接着就是空白部分和 "行和"和 "列和"的关系了 空白连向该行的行和,权值为8 空白连向该列的列和,权值也为8 为什么为8,而不是9,因为流量也可能为0,但是0是不能填的,所以将容量设为8,最后取值的时候加1即可 #include <cstdio

hdu 3338 Kakuro Extension(最大流)

hdu 3338 Kakuro Extension Description If you solved problem like this, forget it.Because you need to use a completely different algorithm to solve the following one. Kakuro puzzle is played on a grid of "black" and "white" cells. Apart

HDU 3338 Kakuro Extension(网络流)

HDU 3338 Kakuro Extension 题目链接 题意:完成如图的游戏,填充数字1-9 思路:网络流的行列模型,把每行每列连续的一段拆分出来建图即可,然后题目有限制一个下限1,所以 每行每列的容量减去相应的数字,然后建图建容量8就好,这样就默认原来容量已经有1了 代码: #include <cstdio> #include <cstring> #include <queue> #include <algorithm> using namespac

HDU 3338 Kakuro Extension

网络最大流 TLE了两天的题目.80次Submit才AC,发现是刘汝佳白书的Dinic代码还可以优化.....瞬间无语..... #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<vector> #include<queue> #include<algorithm> using namespace std; const

hdoj 3338 Kakuro Extension 【经典最大流+输出流量】

题目:hdoj 3338 Kakuro Extension 定义:神级最大流(各种错误无数次,整整一天) 题意:一个游戏,这个游戏给出一个矩阵,有些矩阵里面有两个数,前面一个数表示从下一行到下一个出现数字行的所有数字和(当前这一列),而第二个数表示从下一列到下一个出现数字的列(当前这一行),让你填入满足条件的数字的矩阵(1---9),可以重复(最大流条件). 分析:首先数字可以重复那么确定了可以用最大流来做,建图方法就是列进行出,或者行进列出,前者比较好写. 这个题目还有一个条件就是要求必须有流

hdu 3344 Kakuro Extension Extension

Kakuro Extension Extension Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 468    Accepted Submission(s): 240 Problem Description You know ,I'm a lazy guy and write problem description is a ver

HDU3338 Kakuro Extension(最大流+思维构图)

这道题一定要写一下,卡了好久. 题意: 有黑白两种方格,最上边一行和最左边一列一定是黑色,然后其余的地方有可能是黑色,有可能是白色,和白色相邻的黑色方格里有数字(1个或2个), 现在要求在白色方格里填1~9中的一个数字,使得一个黑色方格下边的数字 = sigma(该黑色方格下边白色方格数字)  这个sigma不是下边全部的白方格, 而是一直往下走一直走到一个黑方格之前所有的白方格,详情见样例1.相同的,黑方格右上角的数字 = sigma(该黑色方格右边白色方格数字). 思路: 可以很容易看出,所

hdu 3338 最大流 ****

题意: 黑格子右上代表该行的和,左下代表该列下的和 链接:点我 这题可以用网络流做.以空白格为节点,假设流是从左流入,从上流出的,流入的容量为行和,流出来容量为列和,其余容量不变.求满足的最大流.由于流量有上下限限制,可以给每个数都减掉1,则填出来的数字范围为0—8, 就可以用单纯的网络流搞定了.求出来再加上就可以了. 这一题主要是在建图 建图: 一共有四类点: 1. 构造源点ST,汇点ED 2. 有行和的格子,即\上面有值的格子,此类节点设为A 3. 空白格,设为B 4. 有列和的格子,即\下

Kakuro Extension (hdu 3338 最大流 建图难)

Kakuro Extension Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1093    Accepted Submission(s): 377 Special Judge Problem Description If you solved problem like this, forget it.Because you nee