51Nod 1296 - 有限制的排列(DP)
阿新 • • 發佈:2018-11-08
【題目描述】
【思路】
做這道題首先要知道一種全排列的生成方式:如果要生成
的全排列,考慮遞推關係,如果現在所有
的排列都是已知的,那麼假設
中的一個排列末尾元素為
,那麼只要把
中所有
的元素都
,然後把
放在第
項,就能構造出一個
的排列,並且前面的
項之間相互大小關係是不變的
首先根據輸入資料處理出一個數組 ,表示相鄰兩項的關係, 表示 , 表示 , 表示無限制,然後
設 表示 數字 在前 個位置,以 為結尾生成的合法排列數目,有如下狀態轉移方程
遞推邊界從 開始,很明顯可以通過字首和來優化這個遞推方程,在遞推過程中用一個 陣列記錄字首和 ,每次用 遞推,然後不斷更新
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1000000007;
const int maxn=5005;
int n,k1,k2;
int p[maxn];//p[x]=-1表示a[x]<a[x+1],p[x]=1表示a[x]>a[x+1],p[x]=0表示都行
int dp[maxn][maxn];
int sum[maxn];
int main(){
scanf("%d%d%d",&n,&k1,&k2);
for(int i=1;i<=k1;++i){
int x;
scanf("%d",&x);
++x;
p[x-1]=1;
p[x]=-1;
}
for(int i=1;i<=k2;++i){
int x;
scanf("%d",&x);
++x;
p[x-1]=-1;
p[x]=1;
}
sum[1]=dp[1][1]=1;
for(int i=1;i<n;++i){
for(int j=1;j<=i+1;++j){
if(p[i]==-1){
dp[i+1][j]=sum[j-1];
}
else if(p[i]==1){
dp[i+1][j]=((sum[i]-sum[j-1])%mod+mod)%mod;
}
else{
dp[i+1][j]=sum[i];
}
}
for(int j=1;j<=i+1;++j){
sum[j]=(sum[j-1]+dp[i+1][j])%mod;
}
}
printf("%d\n",sum[n]);
return 0;
}
可以看到上面的程式碼中 的第一維完全可以省去,進一步優化記憶體
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1000000007;
const int maxn=5005;
int n,k1,k2;
int p[maxn];//p[x]=-1表示a[x]<a[x+1],p[x]=1表示a[x]>a[x+1],p[x]=0表示都行
int dp[maxn];
int sum[maxn];
int main(){
scanf("%d%d%d",&n,&k1,&k2);
for(int i=1;i<=k1;++i){
int x;
scanf("%d",&x);
++x;
p[x-1]=1;
p[x]=-1;
}
for(int i=1;i<=k2;++i){
int x;
scanf("%d",&x);
++x;
p[x-1]=-1;
p[x]=1;
}
sum[1]=dp[1]=1;
for(int i=1;i<n;++i){
for(int j=1;j<=i+1;++j){
if(p[i]==-1){
dp[j]=sum[j-1];
}
else if(p[i]==1){
dp[j]=((sum[i]-sum[j-1])%mod+mod)%mod;
}
else{
dp[j]=sum[i];
}
}
for(int j=1;j<=i+1;++j){
sum[j]=(sum[j-1]+dp[j])%mod;
}
}
printf("%d\n",sum[n]);
return 0;
}