jzoj4019 【雅礼联考DAY02】Path (奥妙重重DP题)

题意

给定一个 n∗ m 的网格,你在左下角 (n,1),你只能往前走或者右拐,障碍和走过的点不能走。求走到 (y,x) 的方案数 mod k 的值。

题别看错了

是指他能一直向右转圈圈,而不是只能向上,向右走。
比赛的时候一丁点想法都没有,dp能力有点小弱。

首先因为他是转着圈到终点的,我们反过来,从终点到起点,可以发现他实际上是一个慢慢变大的矩形。

这就是我们划分状态的依据,设 f [ 0..4 ] [ x ] [ y ] [ r x ] [ r y ] 表示当前考虑了左上角(x,y),右下角(rx,ry)这个矩形的
左上角(从右边走过来),
右上角(从上面走过来),
右下角(从左边走过来),
左下角(从上面走过来)的方案数。 也就是这个点一直在此矩形内运动。

现在考虑转移,因为状态数都是n^4的了,转移必须O(1)。
一个大矩形可以分解成两个小矩形的答案,就是这样:这里写图片描述

再注意一下障碍和初值的问题,见标吧。

看不懂没关系,自己翻题解去吧!
接下来按照列数 + 行数的顺序转移,再滚动优化一下即可。
(这样实际上只需要存三个坐标即可。)
可能还要卡卡常数啥的

#include <cstdio>
#include <iostream>
#include <cstring>
#define dui(x,y) ((x) == ex && (y) == ey)
using namespace std;
typedef long long ll;
const int N = 110;
ll n,m,k,ex,ey;
ll o,f[2][4][N][N][N];
char e[N][N];
int has[N][N];
int xd[N][N];

int main() {
    freopen("t2.in","r",stdin);
    cin>>n>>m>>k>>ey>>ex;
    for (int i = 1; i <= n; i++) {
        scanf("%s",e[i] + 1);
        for (int j = 1; j <= m; j++) {
            has[i][j] = has[i][j-1] + (e[i][j] == '*');
            xd[i][j] = xd[i-1][j] + (e[i][j] == '*');
        }
    }

    for (int s = 2; s <= n + m; s++) {
//      cout<<s - 1<<endl;
        o = 1 - o;
//      memset(f[o],0,sizeof f[o]);
        for (int u = 1; u < s; u++) {
            int v = s - u;
            for (int x = 1; x <= ex && x + u - 1 <= n; x++) {
                for (int y = 1; y <= ey && y + v - 1 <= m; y++)  {
                    int rx = x + u - 1, ry = y + v - 1;
                    if (rx < ex || ry < ey) continue;
                    f[o][0][x][y][rx] = (f[1 - o][0][x][y][rx] + (has[x][ry] - has[x][y-1] == 0) * (f[1 - o][1][x+1][y][rx] + dui(x,ry))) % k;
                    f[o][1][x][y][rx] = (f[1 - o][1][x][y][rx - 1] + (xd[rx][ry] - xd[x-1][ry] == 0) * (f[1 - o][2][x][y][rx] + dui(rx,ry))) % k;
                    f[o][2][x][y][rx] = (f[1 - o][2][x][y + 1][rx] + (has[rx][ry] - has[rx][y-1] == 0) * (f[1 - o][3][x][y][rx - 1] + dui(rx,y))) % k;
                    f[o][3][x][y][rx] = (f[1 - o][3][x + 1][y][rx] + (xd[rx][y] - xd[x-1][y] == 0) * (f[1 - o][0][x][y + 1][rx] + dui(x,y))) % k;
                }
            }
        }
    }
    printf("%lld\n",f[o][3][1][1][n]);
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/81047899