昨天晚上看了一下題目,覺得是DP,後來再看看覺得挺難的。。搞了搞沒搞出來。。
於是看了一下老師給的題解,寫得很簡略。但大致有了個思路,第一次用三維的DP,覺得好神奇。
然後就想着我要寫這道題的題解。
今天上午花了大半個上午,先後換了三種方法,終於做出來了。
========================================================
題目大意:原題用英文說得很拗口,翻譯過來就是1-N N個數的 序列內進行比較,問有幾種排列方式可以出現K個“<”號。
eg:1 3 5 4 2 can be changed to 1<3<5>4>2.
解題思路:
dp[i][k][j] 表示 當有 i 個數,“<” 有 k 個,且該排列方式以小於等於j 的數結尾時有幾種排列方式。
初始化:dp[0][0][0] = 1;
狀態轉移方程:dp[i][k][1] = dp[i-1][k][i-1]
dp[i][k][j] = dp[i][k][j-1] + ( dp[i-1][k][i-1] - dp[i-1][k][j-1] + dp[i-1][k-1][j-1] )
注意點:
1. 題目數據很大,但題目要求將結果%2007輸出,所以在每次算完 dp[i][k][j] 後都將 dp[i][k][j] % 2007 ;
2. 由1 會導致另一個問題,在 “ dp[i-1][k][i-1] - dp[i-1][k][j-1] ” 出現值負數的情況,每次均需將所得 dp[i][k][j] + 2007*x 直至 dp[i][k][j]爲正數。
3. 將對整個三維數組的求解放在外面,則只需進行一次求解。
代碼:
#include<iostream>
#include<memory.h>
#include<cstdio>
using namespace std;
int dp[105][105][105];
int main()
{
dp[0][0][0] = 1;
for(int i = 1 ; i <= 100 ; i++)
{
for(int k = 0; k < i ; k++)
{
dp[i][k][1] = dp[i-1][k][i-1];
for(int j = 2; j <= i ; j++)
{
dp[i][k][j] = dp[i][k][j-1] + dp[i-1][k][i-1] - dp[i-1][k][j-1];
if(k>0) dp[i][k][j] += dp[i-1][k-1][j-1];
if(dp[i][k][j] < 0) dp[i][k][j]+= 2007;
if(dp[i][k][j] >= 2007) dp[i][k][j] %= 2007;
}
}
}
int n,k;
while(scanf("%d%d" , &n , &k)!=EOF)
{
printf("%d\n", dp[n][k][n]);
}
}
==========================================================
心酸過程:
一開始定義dp的狀態爲 i 個數 ,k 個小於號,最後一位爲j,搞了四個for,結果TLE了。
然後後來想到上面的定義狀態的方法,結果WA了,原因是當時沒想到將dp[i][k][j-1]加上去。
然後問了胖丁,胖丁提議搞一個sum數組來存。我覺得思路很正確,但是不知道爲什麼就是得不到正確的結果,可能是狀態轉移方程有誤。
後來我突然想到我之前的方法可用。
最後是你看到的這個。