noi 2009 管道取珠 动态规划

 

这是一道非常精致的递推问题,程序非常短,但很难想到。问题的关键是对目标的转化以及递推状态的设计。

首先对 ∑ai^2 进行转化,将其这样理解:用AB分别表示一种取珠的方法,将结果相同的两种取珠方法(不管这两种取珠方法本身是否相同)记为(A, B),不难发现∑ai^2就是所有这样的(A, B)的对数。

设状态f(a1, b1, a2, b2),如果f(a1, b1, a2, b2) = K,表示存在K对不完全相同的(A, B),使得A方法已经取出了第一个串的前a1个字符及第二个串的前b1个字符,B方法已经取出了第一个串的前a2个字符以及第二个串的前b2个字符,同时A方法与B方法得到的结果相同。

显然,f(a1, b1, a2, b2)可以转移向4个方向:f(a1+1, b1, a2+1, b2)f(a1+1, b1, a2, b2+1)f(a1, b1+1, a2+1, b2)f(a1, b1+1, a2, b2+1),转移可行的前提是对应的字母相同。

最后,f(n, m, n, m)就是问题的答案。递推初态是f(0, 0, 0, 0) = 1.

另外,由于AB的结果相同,显然有a1+b1 = a2+b2,故只要存储其中的三个值即可。算法的时空复杂度均为O(n3)。空间复杂度经优化可到n^2

 转自http://hi.baidu.com/19930705cxjff/blog/item/a1dd92deb7872454ccbf1a82.html

 

 1 #include<iostream>
 2 #include<cmath>
 3 #include<cstring>
 4 #include<cstdio>
 5 using namespace std;
 6 #define MAXN 501
 7 #define MOD 1024523
 8 int dp[2][MAXN][MAXN];
 9 int n,m;
10 char c1[MAXN],c2[MAXN];
11 int main()
12 {
13     memset(dp,0,sizeof(dp));
14     scanf("%d%d",&n,&m);
15     int i,j,k;
16     scanf("%s",c1);
17     scanf("%s",c2);
18     dp[0][0][0]=1;
19     for(k=0;k<n+m;k++)
20     for(i=0;i<=min(k,n);i++)
21     for(j=0;j<=min(k,n);j++)
22     {
23         if(i<n&&j<n&&c1[n-i-1]==c1[n-j-1])
24             dp[(k+1)%2][i+1][j+1]=(dp[k%2][i][j]+dp[(k+1)%2][i+1][j+1])%MOD;
25         if(i<n&&k-j<m&&c1[n-i-1]==c2[m-k+j-1])
26             dp[(k+1)%2][i+1][j]=(dp[k%2][i][j]+dp[(k+1)%2][i+1][j])%MOD;
27         if(k-i<m&&j<n&&c2[m-k+i-1]==c1[n-j-1])
28             dp[(k+1)%2][i][j+1]=(dp[k%2][i][j]+dp[(k+1)%2][i][j+1])%MOD;
29         if(k-i<m&&k-j<m&&c2[m-k+i-1]==c2[m-k+j-1])
30             dp[(k+1)%2][i][j]=(dp[k%2][i][j]+dp[(k+1)%2][i][j])%MOD;
31         //cout<<k<<" "<<i<<" "<<j<<" "<<dp[k%2][i][j]<<endl;
32         dp[k%2][i][j]=0;    
33     }
34     printf("%d\n",dp[(n+m)%2][n][n]);
35     return 0;
36 }

 

posted on 2012-07-16 20:33  myoi  阅读(1102)  评论(0编辑  收藏  举报

导航