每日一題---擺動序列

問題描述
如果一個序列的奇數項都比前一項大,偶數項都比前一項小,則稱爲一個擺動序列。即 a[2i]<a[2i-1], a[2i+1]>a[2i]。
小明想知道,長度爲 m,每個數都是 1 到 n 之間的正整數的擺動序列一共有多少個?
輸入格式
  輸入一行包含兩個整數 m,n。
輸出格式
  輸出一個整數,表示答案。答案可能很大,請輸出答案除以10000的餘數。
樣例輸入
3 4
樣例輸出
14
樣例說明
  以下是符合要求的擺動序列:
  2 1 2
  2 1 3
  2 1 4
  3 1 2
  3 1 3
  3 1 4
  3 2 3
  3 2 4
  4 1 2
  4 1 3
  4 1 4
  4 2 3
  4 2 4
  4 3 4
評測用例規模與約定
  對於 20% 的評測用例,1 <= n, m <= 5;
  對於 50% 的評測用例,1 <= n, m <= 10;
  對於 80% 的評測用例,1 <= n, m <= 100;
  對於所有評測用例,1 <= n, m <= 1000。
題解:
這道題最直接的方法是用dfs一位一位搜索。如果這一位是奇數搜索小於前一位的否則搜索大於前一位的但是這樣的話,複雜度是階乘級別,肯定會超時。然後我們可以想到用記憶化搜索優化dfs用一個記憶數組dp[i][j]維護一下長度爲i最後一位是j的情況有多少種,但是這樣優化還是比較慢,因爲每次不能借用前一種的情況最後我們再想,可以採用二維dp的方法dp[i][j]跟上種方法類似,表示長度爲i最後一位<=j(奇數是小於等於,偶數是大於等於)的情況有多少種然後每次的更新利用上一次更新做到優化最後可以使用O(n2)複雜度完成i和j對應的m和n,m和n都是1e3,所以足夠開一個二維數組,然後開始找狀態轉移方程。
如果i是奇數,說明他要找比上一個數他小的情況就加上dp[i−1][j−1]讓 j 從 n到1枚舉,然後每次要加上一次更新的情況(dp[i][j+1]);如果i是偶數,就要找比一個數大的情況也就是dp[i−1][j+1]讓j 從 1到n枚舉,然後每次更新上一次更新的情況(dp[i][j−1]),所以可以找到狀態方程:

dp[i][j]=(dp[i−1][j−1]+dp[i][j+1])%mod,i爲奇數        
dp[i][j]=(dp[i−1][j+1]+dp[i][j−1])%mod,i爲偶數   
//#include<bits/stdc++.h>
#include<iostream>
#define mod 10000
using namespace std;
int dp[1010][1010];
int main() {
    // m爲長度,n爲數的最大取值範圍
    int m,n;
    cin>>m>>n;
    for(int i=1; i<=n;i++)
        dp[1][i]=n-i+1;//初始化第一位爲i的時候有n-i+1種情況 
    for(int i=2;i<=m;i++)
        if(i&1)//奇數倒着更新 
            for(int j=n;j>=1;j--)
                dp[i][j]=(dp[i-1][j-1]+dp[i][j+1])%mod;
        else//偶數順着更新 
            for(int j=1;j<=n;j++)
                dp[i][j]=(dp[i-1][j+1]+dp[i][j-1])%mod;
    int ans=m&1?dp[m][1]:dp[m][n];
    cout<<ans;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章