滚动数组优化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
*/