【题解】ZJOI2013蚂蚁寻路

  这题强呀……打了10+30暴力之后苦想1h并不会做……于是去看题解。看题解的时候又莫名各种看错,结果看了好久才懂……记录一下血泪史吧。

  这题不难发现走出来的图形就是一个高低高低的城堡型图案,命名为高峰跟低谷的话就是一共有k个低谷和k + 1个高峰,且交替出现。发现其实这个图形是由2 * k + 1个矩形所构成的,我们考虑将这样许多矩形看做转移的方向:f[i][j][p][h]代表以点(i, j)为左下角,当前枚举到的是第p个矩形,且高度为(i - h + 1)所能获得的最大权值。

  转移方程:

    \(f[i][j][p][h] = \left \{ f[i][j - 1][p][h], f[i][j - 1][p][h']] \right \} + s[j][i] - s[j][h - 1]; \)

  其中第一部分代表和j - 1处于同一个矩形当中,第二部分则表示根据p的奇偶性选择<h或>h的转移。第二部分如果枚举,则复杂度太高了难以承受,所以用另一个数组g来保存,追加一维0/1来表示是>还是<中的最大值。然后发现其实 i 这一维是没有参与转移的,所以将这一维去掉,节约空间。期间有一个让我比较纠结的地方,就是

    \( ans = max(f[i][j][k][i], g[i][j][k][i][0]); \)

  起初并没有很理解为什么要将前一部分考虑进来。当高度等于i,不是说明这是一个低谷吗?为什么会符合条件呢?但其实有一种情况下是可以的:当没有低谷的时候,这样是一个符合条件的解。追加辅助数组的dp做的太少了,要多多加油呀ヾ(๑╹◡╹)ノ"

#include <bits/stdc++.h>
using namespace std;
#define INF 999999999
#define maxn 105
int n, m, k, ans = -INF, sum[maxn][maxn];
int f[maxn][maxn][maxn], g[maxn][maxn][maxn][2];

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}
 
void work()
{
    for(int p = 1; p <= k; p ++)
        for(int h = 1; h <= n; h ++)
        {
            f[0][p][h] = -INF;
            g[0][p][h][0] = g[0][p][h][1] = -INF;
        }
        
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
        {
            for(int p = 1; p <= k; p ++)
            {
                for(int h = 1; h <= i; h ++)
                {
                    f[j][p][h] = max(f[j - 1][p][h], g[j - 1][p - 1][h][p % 2]);
                    f[j][p][h] += sum[j][i] - sum[j][h - 1];
                }
                g[j][p][1][0] = -INF;
                for(int h = 2; h <= i; h ++)
                    g[j][p][h][0] = max(g[j][p][h - 1][0], f[j][p][h - 1]);
                g[j][p][i][1] = -INF;
                for(int h = i - 1; h >= 1; h --)
                    g[j][p][h][1] = max(g[j][p][h + 1][1], f[j][p][h + 1]);
            }
            ans = max(ans, max(f[j][k][i], g[j][k][i][0]));
        }
}

int main()
{
    n = read(), m = read(), k = read();
    k = k * 2 + 1;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
        {
            int t = read();
            sum[j][i] += sum[j][i - 1] + t;
        }
    work();
    printf("%d\n", ans);
    return 0;
}

 

posted @ 2018-05-01 20:58  Twilight_Sx  阅读(395)  评论(0编辑  收藏  举报