Sicily 1687 Permutation

昨天晚上看了一下題目,覺得是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數組來存。我覺得思路很正確,但是不知道爲什麼就是得不到正確的結果,可能是狀態轉移方程有誤。

後來我突然想到我之前的方法可用。

最後是你看到的這個。







發佈了22 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章