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
#include
#include
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数组来存。我觉得思路很正确,但是不知道为什么就是得不到正确的结果,可能是状态转移方程有误。

后来我突然想到我之前的方法可用。

最后是你看到的这个。







你可能感兴趣的:(Sicily,permutation)