题目:Sprinklers 2: Return of the Alfalfa
网址:https://www.luogu.com.cn/problem/P6275
这是我第一次真正领略到轮廓线DP的魅力。
经过观察会发现:题目实际上问的是将整个正方形用一条从左上到右下的轮廓线有多少方案。
定义状态:(dp[i,j,0])代表该点的前驱在左侧;(dp[i,j,1])代表该点的前驱在该点的上方。
有:(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})。
vice versa.
C++ AC代码
#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
", (dp[n][n][0] + dp[n][n][1]) % mod);
return 0;
}