51Nod 1296 有限制的排列

2021-07-30 22:53:21 字數 1276 閱讀 3254

acm模版

個人感覺,這個應該算是數字 dp。

先通過處理輸入資料獲取乙個 state,表示每相鄰兩項之間的大小關係,state[i] = 0,表示無特別關係,state[i] = 1 表示第 i 項小於第 i - 1 項,state[i] = 2 表示第 i 項大於第 i - 1 項。

接著搞乙個 dp[i][j] 表示由前 i 個數組成的序列且第 i 位為 j 的合法情況數。在規劃的過程中,針對不同的 state[i],對應不同的狀態轉移(具體轉移方程看**吧),這裡涉及到乙個最後一位數 j 插入序列的思維,可以看做把前邊的每一種排列中大於等於 j 的數 ++,也就可以達到空出 j 這個數將其插入的效果。

在這裡引入乙個 sum[j],表示為前一輪狀態下,最後一位小於等於 j 的情況的和。也就是說,當規劃到第 i 位時,sum[j] 表示前 i - 1 位數組成的序列的合法情況的 dp[i - 1][j] 的字首和。這是為了降低複雜度,將三層迴圈降低為兩層,複雜度從 o(n^3) 降低為 o(n^2)。

最後還使用了乙個資料結構的優化——滾動陣列,降低了記憶體的消耗,直接將二維陣列降低為一維,原本打算將第一維減小到2,構建乙個經典的滾動陣列,可是後來發現由於狀態轉移方程比較特殊,根本沒有直接用到過前一輪的狀態,而是完完全全依賴於 sum[j],所以直接搞成乙個一維的 dp[j] 即可了。

就這樣子,沒毛病了。

#include 

#include

#include

using namespace std;

const int maxn = 5e3 + 10;

const int mod = 1e9 + 7;

int n, k, l;

intstate[maxn], sum[maxn];

int dp[maxn];

int main()

for (int i = 0; i < l; i++)

sum[0] = 0;

dp[1] = sum[1] = 1;

for (int i = 2; i <= n; i++)

else

if (state[i] == 1)

else

}for (int j = 1; j <= i; j++)

}printf("%d\n", sum[n]);

return

0;}

51Nod 1296 有限制的排列

計算整數集合滿足下列條件的的排列個數 在位置a1,a2,ak小於其鄰居 編號從0開始 在位置b1,b2,bl大於其鄰居。輸出符合條件的排列數量mod 1000000007的結果。例如 n 4,a b 符合條件的排列為 2 1 4 3 3 2 4 1 4 2 3 1 3 1 4 2 4 1 3 2 i...

51Nod 1296 構造排列 DP

題目鏈結 題意 給定n,要求構造滿足要求的排列,對於其中的一些成員,值大於左右鄰居,對於另一些成員,值小於左右鄰居。輸出滿足條件的排列種數。思路 設dp i j dp i j dp i j 為前i ii 個數構成的滿足條件的合法排列,且末尾為j jj的個數。那麼對於第i ii個數,我們只需要考慮其與...

51nod 1934 受限制的排列

這題還要判無解真是難受 我們發現我們肯定能確定1的位置,1左右的兩個區間是同理的可以確定出最小值的位置 我們把區間最小值看成給乙個區間 1,構建出笛卡爾樹,就求出了每一次取最小值和最小值左右的區間大小 然後就相當於左右子樹的排列方式,乘上把左右子樹那麼多個元素選出左子樹個數和右子樹個數那麼多的方案數...