幸運號碼-dp

題目:

時間限制:1 秒 空間限制:65536 KB 分值: 20
1個長度爲2N的數,如果左邊N個數的和 = 右邊N個數的和,那麼就是一個幸運號碼。
例如:99、1230、123312是幸運號碼。
給出一個N,求長度爲2N的幸運號碼的數量。由於數量很大,輸出數量 Mod 10^9 + 7的結果即可。
Input
輸入N(1<= N <= 1000)
Output
輸出幸運號碼的數量 Mod 10^9 + 7
Input 示例

 

1
Output 示例
9

題目分析:

N比較小,仔細分析一下題目,有點像動態規劃。想了好久沒整明白遞推方程,沒有完整的寫出來,更解決不了這道題目。

在qq上問了一下,一個人很快就列出了動態方程太牛了,然後自己分析了一下,沒有問題,結果代碼實現,最終也是沒有問題,

這道題目,不能直接求出答案,需要分兩步:第一步,先求出n位數,和爲s的情況種數。這個相信很多人能夠列出狀態方程,類似於揹包吧,

dp[n][s]表示n位,和爲s的號碼個數,方程爲dp[n][s] = 求和(dp[n-1][s-i],i=[0,9])什麼意思呢?也就是當第n位爲0-9時,和爲s的種類和n-1位,和爲s減去0-9的種類有關,是求和的關係。

那麼得到了上面的結果後,如何求得N位的幸運號碼呢?N位,和爲s的種類數知道了,但是2N位要求前N位的首位不能爲0,而後N位無所謂,那麼首位不爲0的個數怎麼確定呢,這裏有個很巧妙的辦法,就是dp[n][s] - dp[n-1][s]這就是第n位爲0的情況,那麼有了這個就直接可以求出所有的幸運號碼了就是dp[n][s]*(dp[n][s] - dp[n-1][s]),s=[0,9*N],求和就是結果。

有了這些分析,就能夠寫程序了,注意,這裏第n位只和第n-1位有關,所以只需要開闢一個2*N*9的空間就行了,滾動數組,並且要int64,否則會溢出。

下面就直接是代碼了,好好體會動態規劃的分析過程,爲何自己第一時間想不到。。。。很感謝這位好友,這裏分享一下思路,以作總結啦。

代碼:

#include <iostream>
#include <cstdlib>
#include <cstring>
//#include <Windows.h>
using namespace std;
const int MAXN = 1010;
const int MAX = 1000000007;

__int64 dp[2][9*MAXN];

int main()
{
	int N;
	while (cin >> N)
	{
		//int time = GetTickCount();
		memset(dp,0,sizeof(dp));
		dp[1][0] = 1;
		for (int i = 0; i <= 9; ++ i)
		{
			dp[0][i] = 1;
		}
		for (int i = 1; i < N; ++ i)
		{
			for (int s = 0; s <= 9 * (i+1); ++ s)
			{
				__int64 sum = 0;
				for (int j = 0; j <= 9; ++ j)
				{
					if (s >= j)
					{
						sum = (sum + dp[(i - 1)%2][s - j])%MAX;
					}
					else
						dp[i%2][s] = 0;
				}
				dp[i%2][s] = sum;
			}
		}
		__int64 ans = 0;
		for (int i = 0; i <= N * 9; ++ i)
		{
			ans = (ans + dp[(N+1)%2][i]*(dp[(N+1)%2][i] - dp[N%2][i]))%MAX;
		}

		cout << ans << endl;
	}
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章