zoukankan      html  css  js  c++  java
  • 滚动数组优化dp

    滚动数组优化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
    
    */
    
  • 相关阅读:
    生活感悟(一)
    DOM数据制作(采用卫星遥感图像数据制作)
    对话框显示前的操作
    sqlHelper中DataReader的关闭问题
    整数的取余运算
    C#中的字符串格式String.Format
    SQL分页查询
    级联删除与更新的例子
    C#中的运算符重载(以重载+为例)
    [高效编程读书笔记]用readonly而不是const
  • 原文地址:https://www.cnblogs.com/OvOq/p/14724864.html
Copyright © 2011-2022 走看看