網址:https://www.luogu.com.cn/problem/P6275ios
這是我第一次真正領略到輪廓線DP的魅力。c++
通過觀察會發現:題目實際上問的是將整個正方形用一條從左上到右下的輪廓線有多少方案。spa
定義狀態:\(dp[i,j,0]\)表明該點的前驅在左側;\(dp[i,j,1]\)表明該點的前驅在該點的上方。code
有:\(dp[i,j,0]=dp[i,j-1,0]*2^{sum[i,j]}\),若是該位置沒有障礙物,那麼還應加上\(dp[i,j,0]=dp[i,j-1,1]*2^{sum[i,j]-1}\)。get
vice versa.string
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; const int SIZE = 2000 + 5, mod = 1e9 + 7; long long n, p[10000000], s[SIZE][SIZE], dp[SIZE][SIZE][2]; bool map[SIZE][SIZE]; int main() { scanf("%d", &n); p[0] = 1; for(int i = 1; i <= n * n; ++ i) p[i] = p[i - 1] * 2 % mod; char str[SIZE]; memset(s, 0, sizeof(s)); memset(dp, 0, sizeof(dp)); for(int i = 1; i <= n; ++ i) { scanf("%s", str + 1); for(int j = 1; j <= n; ++ j) { if(str[j] == '.') { map[i][j] = true; s[i][j] = 1; } else map[i][j] = false; s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]; } } for(int i = 1; i <= n; ++ i) dp[0][i][0] = 1; for(int i = 1; i <= n; ++ i) dp[i][0][1] = 1; for(int i = 1; i <= n; ++ i) { for(int j = 1; j <= n; ++ j) { dp[i][j][0] = dp[i][j - 1][0] * p[s[i][j] - s[i][j - 1]] % mod; dp[i][j][1] = dp[i - 1][j][1] * p[s[i][j] - s[i - 1][j]] % mod; if(map[i][j]) { dp[i][j][0] = (dp[i][j][0] + dp[i][j - 1][1] * (p[s[i][j] - s[i][j - 1] - 1])) % mod; dp[i][j][1] = (dp[i][j][1] + dp[i - 1][j][0] * (p[s[i][j] - s[i - 1][j] - 1])) % mod; } } } printf("%d\n", (dp[n][n][0] + dp[n][n][1]) % mod); return 0; }