51NOD 1296-dp

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1296


题意就是一个 1--n 的序列,然后给你一些数,要再这些位置比旁边大,另一些数,这些位置要比旁边的小。。

题意就是这样,之前做的题拿出来复习。。


dp思路是 dp[i][j]  表示 前i个数最后一位是j是什么的时候有多少可行方案(是不是有点像数位dp的思路。。。)

做一个标记来标记每一位和前一位的大小关系。



那么分三种情况考虑一下:

第i位一是个普通位  ,那么dp[i][j]就是dp[i-1][1~(i-1)]所有的答案和,为什么呢,因为可以看做把前面的每一种安排比j大的都加1就好了。。这样也不影响原来的合法性


第i位 要比前一位低,那么明显不能都加1 了,因为不一定满足第i位合法了,所以其实保证第i-1位大于等于j,也就是dp[i-1][j~(i-1)]所有答案的和,


第i位 要比前一位高, 同理,就是dp[i-1][1~j-1]所有答案的和,


由于上面的转移方法,所以我们再加一个辅助的前缀和数组就可以了。


代码如下:


#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
using namespace std;

int dp[5001][5001];
int sum[5001];
int a[5001];
int b[5001];
int n,na,nb;
int ans;
const int mo=1e9+7;

void DP(){
    ans=0;
    memset(dp,0,sizeof(dp));
    dp[1][1]=1;
    sum[1]=1;
    for(int i=2;i<=n;i++){
        for(int j=1;j<=i;j++){
            if(a[i]==1){
                dp[i][j]=sum[j-1];
            }
            else if(a[i]==-1){
                dp[i][j]=(sum[i-1]-sum[j-1]+mo)%mo;
            }
            else{
                dp[i][j]=sum[i-1];
            }
        }
        for(int j=1;j<=i;j++){
            sum[j]=((long long)sum[j-1]+dp[i][j])%mo;
        }
    }
    for(int i=1;i<=n;i++){
        ans=(ans+dp[n][i])%mo;
    }
    cout<<ans%mo<<endl;
}
int main()
{
    while(cin>>n>>na>>nb){
        int x;
        for(int i=0;i<na;i++){
            scanf("%d",&x);
            ++x;
            a[x]=1;
            a[x+1]=-1;
        }
        memset(b,0,sizeof(b));
        for(int i=0;i<nb;i++){
            scanf("%d",&x);
            ++x;
            a[x]=-1;
            a[x+1]=1;
        }
        DP();
    }
    return 0;
}


你可能感兴趣的:(51NOD 1296-dp)