題目:
輸入N(1<= N <= 1000)
輸出幸運號碼的數量 Mod 10^9 + 7
1
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;
}
}