HDU 5791 (DP)

Alice gets two sequences A and B. A easy problem comes. How many pair of sequence A' and sequence B' are same. For example, {1,2} and {1,2} are same. {1,2,4} and {1,4,2} are not same. A' is a subsequence of A. B' is a subsequence of B. The subsequnce can be not continuous. For example, {1,1,2} has 7 subsequences {1},{1},{2},{1,1},{1,2},{1,2},{1,1,2}. The answer can be very large. Output the answer mod 1000000007.
 

Input
The input contains multiple test cases.

For each test case, the first line cantains two integers N,M(1N,M1000). The next line contains N integers. The next line followed M integers. All integers are between 1 and 1000.
 

Output
For each test case, output the answer mod 1000000007.
 

Sample Input
 
  
3 21 2 32 13 21 2 31 2

这道题目很像求最长子序列的问题(用DP数组解决),但是这道题是要求两个AB数列当中数列A和数列B的非空子数列有多少对是相同的.

确定DP方程: 

DP[i][j]表示A串前i个字符和B串前j个字符之间由多少相同的子序列

两种思路:

1.用123    12来说明

两个数组元素 (从1开始) 1 2 3  
  0 0   0 0  
1 0 1 1 1  
2 0 1 3 3  
 e[i+1][j+1]=(e[i+1][j]+e[i][j+1]-e[i][j]+N)%N;//去重就是上图中红蓝L型线相交的元素(用了两遍),此时是I=2,J=2;
            if(a[i]==b[j])
                e[i+1][j+1]=(e[i+1][j+1]+e[i][j]+1)%N; //如果当前字符相等,那么前面的dp[i][j]就能和这个新的相等字符匹配一遍,所以需要加上dp[i-1][j-1]+1;
全部代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll N=1e9+7;
int e[1005][1005];
int main(){
    int n,m;
    int a[1005],b[1006];
    while(cin>>n>>m){
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(e,0,sizeof(e));
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        for(int i=0;i<m;i++)
            scanf("%d",&b[i]);
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
        {
            e[i+1][j+1]=(e[i+1][j]+e[i][j+1]-e[i][j])%N;
            if(a[i]==b[j])
                e[i+1][j+1]=(e[i+1][j+1]+e[i][j]+1)%N;
        }
        cout<<e[n][m]<<endl;
//        for(int i=1;i<=n;i++)
//        {
//                for(int j=1;j<=m;j++)
//                    cout<<e[i][j]<<"  ";
//                cout<<endl;
//        }

    }
}

2.另一种思路找出状态转移方程

dp[i][j]表示的是序列A前i个数字和序列B前j个数字的公共子序列的总个数,那么的dp公式就可以这么表示

(表示不好推)

理解一下此公式若最尾部的a[i]和b[j]相等的话,那么单独的a[i]和b[j]组成1个相同子序列。

同时我们可以想一下之前的前i-1个子序列和前j-i个子序列再加上a[i]又能组成dp[i-1]个公共子序列。

当然了,包含第i个数字的前i个序列a和前j-1个数字的序列b以及包含第j个数字的前j个序列b和前i-1个数字的序列a也要算上。

所以把上面3种情况一合并就是前i-1个子序列a和前j个子序列b的公共子序列个数加上前i个子序列a和前j-1个子序列b的公共子序列个数再加1。

这里包含了2倍的dp[i-1][j-1]个数。

如果a[i]和b[j]不相等的话,减去一个dp[i-1][j-1]个数即可。

本题中另一个注意点就是dp[i][j]可能为负,此时的处理方法是加上一个MOD


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll N=1e9+7;
int e[1005][1005];
int main(){
    int n,m;
    int a[1005],b[1006];
    while(cin>>n>>m){
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(e,0,sizeof(e));
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        for(int i=0;i<m;i++)
            scanf("%d",&b[i]);
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
        {
            e[i+1][j+1]=(e[i+1][j]+e[i][j+1]-e[i][j]+N)%N;
            if(a[i]==b[j])
                e[i+1][j+1]=(e[i+1][j+1]+1)%N;
            else
                e[i+1][j+1]=(e[i+1][j]+e[i][j+1]-e[i][j]+N)%N;
        }
//        for(int i=1;i<=n;i++)
//        {
//                for(int j=1;j<=m;j++)
//                    cout<<e[i][j]<<"  ";
//                cout<<endl;
//        }

    }
}



猜你喜欢

转载自blog.csdn.net/c___c18/article/details/80680928