[Luogu2045] 方格取数加强版

题目描述

给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大

输入输出格式

输入格式:

第一行两个数n,k(1<=n<=50, 0<=k<=10)

接下来n行,每行n个数,分别表示矩阵的每个格子的数

输出格式:

一个数,为最大和

输入输出样例

输入样例#1:
复制

3 1
1 2 3
0 2 1
1 4 2

输出样例#1: 复制

11

说明

每个格子中的数不超过1000



拆点, 费用流。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define reg register

inline char gc()
{
    static const int BS = 1 << 22;
    static unsigned char buf[BS], *st, *ed;
    if (st == ed) ed = buf + fread(st = buf, 1, BS, stdin);
    return st == ed ? EOF : *st++;
}
#define gc getchar
inline int read()
{
    int res=0;char ch=gc();bool fu=0;
    while(!isdigit(ch)){if(ch==‘-‘)fu=1;ch=gc();}
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=gc();
    return fu?-res:res;
}
#define N 5010
#define M 200010
int n, k;
int a[55][55];
int S, T;
int ans;

struct edge {
    int nxt, to, flow, val;
}ed[M];
int head[N], cnt = 1;
inline void add(int x, int y, int z, int c)
{
    ed[++cnt] = (edge){head[x], y, z, c};
    head[x] = cnt;
    ed[++cnt] = (edge){head[y], x, 0, -c};
    head[y] = cnt;
}

inline int id(int i, int j, int k) {
    return (i - 1) * n + j + k * n * n;
}
int incf[N], dis[N], pre[N];
bool ex[N];

inline bool spfa()
{
    memset(dis, 0xcf, sizeof dis);
    memset(ex, 0, sizeof ex);
    queue <int> q;
    q.push(S);
    dis[S] = 0;
    incf[S] = 1 << 25;
    while(!q.empty())
    {
        int x = q.front();q.pop();
        ex[x] = 0;
        for (reg int i = head[x] ; i ; i = ed[i].nxt)
        {
            if (ed[i].flow) {
                int to = ed[i].to;
                if (dis[to] < dis[x] + ed[i].val) {
                    dis[to] = dis[x] + ed[i].val;
                    pre[to] = i;
                    incf[to] = min(incf[x], ed[i].flow);
                    if (!ex[to]) ex[to] = 1, q.push(to);
                }
            }
        }
    }
    return dis[T] != 0xcfcfcfcf;
}

inline void update()
{
    int x = T;
    while(x != S)
    {
        int i = pre[x];
        ed[i].flow -= incf[T];
        ed[i^1].flow += incf[T];
        x = ed[i^1].to;
    }
    ans += dis[T] * incf[T];
}

int main()
{
    n = read(), k = read();
    for (reg int i = 1 ; i <= n ; i ++)
        for (reg int j = 1 ; j <= n ; j ++)
            a[i][j] = read();
    S = 1, T = 2 * n * n;
    for (reg int i = 1 ; i <= n ; i ++)
        for (reg int j = 1 ; j <= n ; j ++) {
            add(id(i, j, 0), id(i, j, 1), 1, a[i][j]);
            add(id(i, j, 0), id(i, j, 1), k - 1, 0);
            if (j + 1 <= n) add(id(i, j, 1), id(i, j + 1, 0), k, 0);
            if (i + 1 <= n) add(id(i, j, 1), id(i + 1, j, 0), k, 0);
        }
    while(spfa()) update();
    printf("%d\n", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/BriMon/p/9559418.html

时间: 2024-08-01 06:19:24

[Luogu2045] 方格取数加强版的相关文章

Luogu1006 传纸条 与 Luogu P2045方格取数加强版 (费用流)

Luogu1006 传纸条 与 Luogu P2045方格取数加强版 其实就是这几道题 在一个有m*n 个方格的棋盘中 每个方格中有一个正整数 现要从在方格中从左上角到右下角取数,只能向右或向下走 每走到一个格子就可以把这个位置上的数取走(下次经过就没有了) 1.让你走1次,求取出的数的总和最大是多少 2.让你走2次,求取出的数的总和最大是多少 3.让你走k次,求取出的数的总和最大是多少 对于第一问,十分显然. 设\(f[i][j]\)表示\(i\)行\(j\)列的最大价值,转移即可. 第二问,

题解【luogu2045 方格取数游戏加强版】

Description 给出一个 \(n*n\) 的矩阵,每一格有一个非负整数 \(A_{i,j}\) ,(\(A_{i,j} <= 1000\))现在从 \((1,1)\) 出发,可以往右或者往下走,最后到达 \((n,n)\) ,每达到一格,把该格子的数取出来,该格子的数就变成 \(0\) ,这样一共走 \(K\) 次,现在要求 \(K\) 次所达到的方格的数的和最大 Solution 一条边 \((a,b)\) 表示容量为 \(a\) ,费用为 \(b\) . 把每个点拆成两个点,入点和出

poj 3422 洛谷P2045 K取方格数(方格取数加强版)

Description: 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大 Input: 第一行两个数n,k(1<=n<=50, 0<=k<=10) 接下来n行,每行n个数,分别表示矩阵的每个格子的数 Output: 一个数,为最大和 思路:仍旧是拆点 因为每个点都有一个限制K和一个价

P2045 方格取数加强版

题目描述 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大 输入输出格式 输入格式: 第一行两个数n,k(1<=n<=50, 0<=k<=10) 接下来n行,每行n个数,分别表示矩阵的每个格子的数 输出格式: 一个数,为最大和 输入输出样例 输入样例#1: 3 1 1 2 3 0 2

[洛谷P2045]方格取数加强版

题目大意:有一个n*n的矩阵,每个格子有一个非负整数,规定一个人从(1,1)开始,只能往右或下走,走到(n,n)为止,并把沿途的数取走,取走后数变为0.这个人共取n次,求取得的数的最大总和. 解题思路:由于取多少次不确定,所以不能用dp. 我们发现,一个格子只能从左边或上面走来,且数只能取到一次,那么我们可以把此题转化为最大费用最大流问题.首先拆点,将一个点拆成x和y,然后从x到y连一条容量为1,流量为x(x为这格的数)的边,然后再连一条容量为inf,费用为0的边,这样即可保证一个点可以走多次,

Luogu P2045 方格取数加强版 题解

闲扯 所以我还是不会做网络流啊... 打个模板多轻松啊,为什么还要建图呢,天空这么蓝,森林那么绿,这个世界多么美好啊! 建图的套路感觉好多啊..还是慢慢学吧.. Solution 题目分析/建图 因为限制了方向,同时还限制了每一个最多取一次,要求和最大,想到了什么?什么都没想到 最大费用最大流! 因为每个点只能选一次,所以我们考虑把这个点拆开,变成一个入点,一个出点,然后在入点和出点之间连上一条流量为 \(1\) ,费用为 \(val_i\) 的边.但是每一个的数取了后还是可以经过这个位置的,所

[luoguP2045] 方格取数加强版(最小费用最大流)

传送门 水题 ——代码 1 #include <queue> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #define N 51 6 #define M 100001 7 #define INF 1e9 8 #define min(x, y) ((x) < (y) ? (x) : (y)) 9 10 int n, k, s, t, cnt, tot, sum; 11

【Luogu】P2045方格取数加强版(最小费用最大流)

题目链接 通过这题我学会了引诱算法的行为,就是你通过适当的状态设计,引诱算法按照你想要它做的去行动,进而达到解题的目的. 最小费用最大流,首先将点拆点,入点和出点连一条费用=-权值,容量=1的边,再连费用=0,容量=INF的边,跑最小费用最大流即可. #include<cstdio> #include<cctype> #include<algorithm> #include<cstring> #include<cstdlib> #include&

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

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