Codeforces570E Pig and Palindromes dp+滚动数组

题目链接:传送门

题目大意:
给出一个 N × M N×M N×M的字符矩阵,求从左上角到右下角、每次只能往右或者往下走,经过的路径上所有字符恰好为一个回文串的方案数。
( N , M ≤ 500 ) (N, M≤500) (N,M500)

每次走的时候无后效性,想到使用 d p dp dp
不妨让左上角和右下角同时开始走。
d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示当前共走 i i i次,左上角到第 j j j行,右下角到第 k k k行,且所有字符满足题意的方案数。
由于走的次数确定,起始点确定,到达的行数确定,
因此珂以算出列数:左上角到第 i − j + 1 i-j+1 ij+1列,右下角到第 n + m − i − k + 1 n+m-i-k+1 n+mik+1列。
y 1 = i − j + 1 y1=i-j+1 y1=ij+1 y 2 = n + m − i − k + 1 y2=n+m-i-k+1 y2=n+mik+1
( j , y 1 ) (j,y1) (j,y1) ( k , y 2 ) (k,y2) (k,y2)的值相等时,珂以从 ( j , k + 1 ) (j,k+1) (j,k+1) ( j − 1 , k ) (j-1,k) (j1,k) ( j − 1 , k + 1 ) (j-1,k+1) (j1,k+1) 走过来。
然后转移方程就比较显然了,具体见代码。

另外,此题走 i i i次的情况只能从走 i − 1 i-1 i1次的情况推过来。
因此珂以用滚动数组优化空间。

时间复杂度: O ( n 3 ) O(n^3) O(n3)
空间复杂度: O ( n 2 ) O(n^2) O(n2)

丑陋的代码:

#include<stdio.h>
#include<cstring>
#include<algorithm>
#define re register int
using namespace std;
typedef long long ll;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x*f;
}
inline char GetChar() {
	char ch=getchar();
	while(ch<'a' || ch>'z')	ch=getchar();
	return ch;
}
const int Size=505;
const int INF=0x3f3f3f3f;
const ll mod=1e9+7;
char a[Size][Size];
ll dp[2][Size][Size];
int main() {
	int n=read();
	int m=read();
	for(re i=1; i<=n; i++) {
		for(re j=1; j<=m; j++) {
			a[i][j]=GetChar();
		}
	}
	//特判一下
	if(a[1][1]!=a[n][m]) {
		putchar('0');
		return 0;
	}
	dp[1][1][n]=1;
	int sum=(n+m)>>1;
	for(re i=2; i<=sum; i++) {
		for(re j=0; j<=n; j++) {
			for(re k=0; k<=n; k++) {
				dp[i&1][j][k]=0;
			}
		}
		for(re j=1; j<=n; j++) {
			for(re k=n; k>n-i; k--) {
				if(j>k)	continue;
				//y1,y2表示列数
				int y1=i-j+1,y2=n+m+1-i-k;
				//由于左上角不能向上走,右下角不能向下走,因此y1>y2时就应跳过
				if(y1>y2)	continue;
				if(a[j][y1]!=a[k][y2])	continue;
				dp[i&1][j][k]=(dp[!(i&1)][j][k]+dp[!(i&1)][j][k+1]+dp[!(i&1)][j-1][k]+dp[!(i&1)][j-1][k+1])%mod;
			}
		}
	}
	ll ans=0;
	int stp=sum&1;
	for(re i=1; i<=n; i++) {
		ans=(ans+dp[stp][i][i])%mod;
		if((n+m)&1) {
			ans=(ans+dp[stp][i][i+1])%mod;
		}
	}
	printf("%I64d",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值