[USACO20OPEN]Sprinklers 2: Return of the Alfalfa -- 轮廓线DP初识

题目: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;
}
原文地址:https://www.cnblogs.com/zach20040914/p/13727071.html