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; }
时间: 2024-11-13 06:46:32