1008考试T3

1008考试T3

​ 题目大意:

​ 给定一张𝑛×𝑚的网格图,A想从(1,1)走到(1,𝑚),B想从(𝑛,1)走到(𝑛,𝑚)。二者都只能向右或是向上下移动,网格图有障碍不能经过,一个空地若被其中一人经过就不能被另一人经过。求方案数对1000000007取模的结果。"."表示空地,"#"表示障碍。对于100%的数据,1≤𝑛,𝑚≤300。

​ 显然DP。应该算线性DP吧。

​ 考场上不会做,打了个20分的缩索。

\(f[i][j][k]\)表示当前在第\(i\)列,\(A\)\((j, i)\), \(B\)\((k, i)\),两人不相交路径的方案数。

​ 那么转移很简单:\(f[i][j][k] = \displaystyle \sum_{p} \sum_{q} f[i - 1][p][q]\)\(p, q\)是可以到达\(j, k\)的点,也就是说它们之间没有障碍物。显然第一维可以去掉。

\(up[i][j], down[i][j]\)分别表示点\((i, j)\)往上走/往下走可以到达哪一行,中间没有障碍物。

​ 不过这么做是\(O(n^5)\)的做法,过不了,我们可以用前缀和优化一下。

\(g[i][j]\)表示\(f[i][j]\)的一维前缀和,\(h[i][j]\)表示二维前缀和。复杂度\(O(n ^ 3)\)

#include <bits/stdc++.h>
    
using namespace std;
    
inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}
    
const int N = 305, mod = 1000000007;
int n, m, ans;
int up[N][N], down[N][N], f[N][N], g[N][N], h[N][N];
char ch[N][N];

void make_prefix() {
    for(int i = 1;i <= n; i++)
        for(int j = 1;j <= n; j++) g[i][j] = (g[i - 1][j] + f[i][j]) % mod;
    for(int i = 1;i <= n; i++)
        for(int j = 1;j <= n; j++) h[i][j] = (h[i][j - 1] + g[i][j]) % mod;
}

int query(int x, int l, int r) { return (g[r][x] - g[l - 1][x] + mod) % mod; }

int query(int x, int xx, int y, int yy) { return (((h[xx][yy] + h[x - 1][y - 1]) % mod - (h[xx][y - 1] + h[x - 1][yy]) % mod) + mod) % mod; }

int main() {

    n = read(); m = read();
    for(int i = 1;i <= n; i++) cin >> (ch[i] + 1);
    for(int j = 1;j <= m; j++) {
        up[0][j] = 0;
        for(int i = 1;i <= n; i++) up[i][j] = ch[i][j] == '#' ? i : up[i - 1][j];
        down[n + 1][j] = n + 1;
        for(int i = n;i >= 1; i--) down[i][j] = ch[i][j] == '#' ? i : down[i + 1][j]; 
    }
    f[1][n] = 1; make_prefix();
    for(int i = 1;i <= m; i++) {
        memset(f, 0, sizeof(f));
        for(int j = 1;j <= n; j++) {
            if(ch[j][i] == '#' || ch[j][i + 1] == '#') continue;
            int tmp = 0, l = up[j][i] + 1, r = down[j][i] - 1;
            for(int k = j + 1;k <= n; k++) {
                if(ch[k][i] == '#') { tmp = 0; continue; }
                tmp = (tmp + query(k, l, min(r, k - 1))) % mod;
                if(ch[k][i + 1] != '#') f[j][k] = (tmp + query(l, min(r, k - 1), k + 1, down[k][i] - 1)) % mod;
            }
        }
        make_prefix();
    }
    printf("%d", f[1][n]);
    
    return 0;
}
posted @ 2020-10-08 20:57  C锥  阅读(94)  评论(1编辑  收藏  举报