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;
}