码迷,mamicode.com
首页 > 编程语言 > 详细

滚动数组优化dp

时间:2021-05-04 15:23:04      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:循环   直接   --   char   using   假设   问题   若是   typedef   

滚动数组优化dp

CF570E Pig and Palindromes

原题链接

题意:

给定一个n*m(n,m <= 500)的字符矩阵,从(1,1)走到(n,m),每次只能向右和向下走,那么有多少种走法可以组成一个回文串。

思路:

由于形成的是回文串,我们可以假设有两个点,点A从(1,1)出发,点B从(n,m)出发,每次转移的时候当两个点所在的位置字符相等才会转移。

很像传纸条问题,借鉴那道题的dp思路可得:

dp[len] [x1] [y1] [x2] [y2] 表示一共走了len步,点A到达(x1,y1),点B到达(x2,y2)的方案数。

对于转移的话:点A可以向右走和向下走,点B可以向左走和向上走,两两组合一共有4种方案,可以用一个方向数组来记录下来,简化代码。

虽然n只有500,但是这个转移无论是从时间还是空间上都是过不去的。

1.我们很容易发现,知道了该点的x和len,就可以计算出y,所以关于y的那一维就可以省略掉。

2.转移的过程中只用到了上一维,可以用滚动数组来优化。

要注意的是,每次做外层循环的时候,都要清空当前状态。

转移的时候细节比较多。

再就是最后统计答案的时候,由于回文串有奇数长度和偶数长度的,两者对应的dp状态是不同的,都要统计上。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=510,mod=1e9+7;
char mp[510][510];
int dp[2][510][510];
int n,m;
int nx[]={0,0,-1,-1};
int ny[]={0,1,1,0};
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>mp[i]+1;
	int now=0;
	if(mp[1][1]==mp[n][m]) dp[now][1][n]=1;
	for(int len=1;len<=(n+m-2)/2;len++){
		now^=1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				dp[now][i][j]=0;
		for(int x1=1;x1-1<=len&&x1<=n;x1++){
			for(int x2=n;n-x2<=len&&x2>=1;x2--){
				int y1=1+len-(x1-1),y2=m-len+(n-x2);
				if(mp[x1][y1]==mp[x2][y2]){
					for(int k=0;k<4;k++){
						dp[now][x1][x2]=(dp[now][x1][x2]+dp[now^1][x1+nx[k]][x2+ny[k]])%mod;
					}
				}
			}
		}
	}
	int res=0;
	for(int i=1;i<=n;i++)
		res=(res+dp[now][i][i])%mod;
	if((n+m)%2){
		for(int i=1;i<n;i++)
			res=(res+dp[now][i][i+1])%mod;
	}
	cout<<res<<endl;
	return 0;
}

Concerts

原题链接

题意:

求A串在B串出现的次数,给出了a数组,a[i]表示若是使用了i+‘A’这个字母,a[i]个后才能使用下一个字母。

思路:

O(n*k)的dp比较好想、

dp[i] [j]表示匹配到了A串的第i个位置和B串的第j个位置的方案数。

枚举顺序为外层循环i,内层循环j。因为限制数组a[i]的存在,需要记录上一个字母选的哪个。

转移的话:

第j个不使用时,直接等于dp[i-1] [j].

第j个使用的条件为当前位置的两个字符相同,并且长度大于上一个字符的限制条件。

1e5*300的数组可以考虑滚动数组优化,优化的时候要清空当前数组,避免影响答案。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10,mod=1e9+7;
int dp[2][maxn];
int a[26],n,k;
char s1[1010],s2[maxn];
int main(){
	
	cin>>k>>n;
	
	for(int i=0;i<26;i++) cin>>a[i];
	
	cin>>s1+1>>s2+1;
	
	for(int i=0;i<=n;i++) dp[0][i]=1;
	
	for(int i=1;i<=k;i++){
		for(int q=0;q<=n;q++)
			dp[i&1][q]=0;
		for(int j=1;j<=n;j++){
			dp[i&1][j]=(dp[i&1][j-1])%mod;
			if(s1[i]==s2[j]&&j-1-a[s1[i-1]-‘A‘]>=0){
				dp[i&1][j]=(dp[i&1][j]+dp[(i-1)&1][j-1-a[s1[i-1]-‘A‘]])%mod;
			}
		}
	}
	
/*	for(int i=1;i<=k;i++)
		for(int j=1;j<=n;j++){
			cout<<dp[i&1][j]<<" ";
			if(j==n) puts("");
		}*/
	cout<<dp[k&1][n]%mod<<endl;
	return 0;
}
/*
2 10
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
AB
ABBBBABBBB

1 1 1 1 1 2 2 2 2 2
1 1 2 3 4 4 5 7 9 11

*/

滚动数组优化dp

标签:循环   直接   --   char   using   假设   问题   若是   typedef   

原文地址:https://www.cnblogs.com/OvOq/p/14724864.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!