藍橋杯(演算法訓練)——K好數
演算法描述
問題描述
如果一個自然數N的K進製表示中任意的相鄰的兩位都不是相鄰的數字,那麼我們就說這個數是K好數。求L位K進位制數中K好數的數目。例如K = 4,L = 2的時候,所有K好數為11、13、20、22、30、31、33 共7個。由於這個數目很大,請你輸出它對1000000007取模後的值。
輸入格式
輸入包含兩個正整數,K和L。
輸出格式
輸出一個整數,表示答案對1000000007取模後的值。
樣例輸入
4 2
樣例輸出
7
資料規模與約定
對於30%的資料,KL <= 106;
對於50%的資料,K <= 16, L <= 10;
對於100%的資料,1 <= K,L <= 100。
思考過程
ps:題目考點是動態規劃,但是我還沒有去查動態規劃的內容,在此先記錄一下自己的思考過程
首先以K=5,L=3為例
先畫了樹狀圖,將所有情況列出。
將第一位設為①,0①表示第1位上的0,以此類推。
列出後將樹狀圖的情況簡化如下(數值代表有幾種情況):
第三層:
0③ = 1;
1③ = 1;
2③ = 1;
3③ = 1;
4③ = 1;
第二層:
0② = 0③ + 2③ + 3③ + 4③;
1② = 1③ + 3③ + 4③;
2② = 0③ + 2③ + 4③;
3② = 0③ + 3③ + 4③;
4② = 0③ + 1③ + 2③ + 4③;
第一層:
0① = 0; 因為首位不可能為0
1① = 1② + 3② + 4②;
2① = 0② + 2② + 4②;
3① = 0② + 3② + 4②;
4① = 0② + 1② + 2② + 4②;
由此規律想到可以列一個k行l列的二維陣列,每一行的值就是靠上述規律得來的。
建立二維陣列後,首先把最後一列的數值填滿為1.
(以K=6, L=4為例)得到陣列
Ⅰ Ⅱ Ⅲ Ⅳ
① 0 0 0 1
② 0 0 0 1
③ 0 0 0 1
④ 0 0 0 1
⑤ 0 0 0 1
⑥ 0 0 0 1
然後倒著從Ⅲ列即倒數第二列開始按照規律填充陣列
如果是第①行的數,就需要除去第Ⅳ列第②行的數,
如果是第⑥行的數,就需要除去第Ⅳ列第⑤行的數,
剩下的行數需要除去第Ⅳ列本行數減一和加一行的數。
將剩下的數相加,得到第Ⅲ列的數字如下
Ⅰ Ⅱ Ⅲ Ⅳ
① 0 0 5 1
② 0 0 4 1
③ 0 0 4 1
④ 0 0 4 1
⑤ 0 0 4 1
⑥ 0 0 5 1
按照上述規律,得到第Ⅱ列數和第Ⅰ列數後,陣列變為:
Ⅰ Ⅱ Ⅲ Ⅳ
① 97 22 5 1
② 74 17 4 1
③ 79 18 4 1
④ 79 18 4 1
⑤ 74 17 4 1
⑥ 97 22 5 1
最後,除去第①行第Ⅰ列的數字,因為首位數字不能為0,將其他第Ⅰ列的數字加和,就得到了所有的K好數的個數。
所以當K=6,L=4時,得到結果為74+79+79+74+97=403
注意事項
- 掌握指標申請動態二維陣列的方法
- 要注意求餘,否則會因數值大小超過限定長度而產生錯誤
- 上述求餘時僅在最後求sum的值時求餘是不夠的,在陣列填充時就需要求餘了
- 好好學數學啊我哭著,抽象模型出來太難了,這個題卡了我將近兩個小時,最開始一直在考慮排列組合但是總感覺不太對勁兒,後面想到用陣列表示也是靈機一動,最開始還想用算式列出來但是發現太不可行了,L=3還能勉強寫出來到了L=4的時候就徹底暈了。
程式碼表示
/*
AUTHER:0_Re5et
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 100
int main()
{
int k, l;
int i, j;
int Lcir, Hcir;
long int sum=0;
long int **p;
scanf("%d %d", &k, &l);
p = (long int**)malloc(sizeof(long int*) * k);
for(i=0; i<k; i++)
{
p[i] = (long int*)malloc(sizeof(long int) * l);
}
// 申請二維陣列
for(i=0; i<k; i++)
{
for(j=0; j<l; j++)
{
p[i][j]=0;
}
}
// 陣列初始化
for(i=0; i<k; i++)
{
p[i][l-1] = 1;
}
// 將最後一列的值設為1
for(Lcir=l-2; Lcir>=0; Lcir--)
{
// 列迴圈
for(Hcir=0; Hcir<k; Hcir++)
{
// 行迴圈
if(Hcir == 0)
{
for(i=0; i<k; i++)
{
if(i != 1)
{
p[0][Lcir] = p[0][Lcir] + p[i][Lcir+1];
p[0][Lcir] = p[0][Lcir] % 1000000007;
}
else
continue;
}
}
else if(Hcir == k-1)
{
for(i=0; i<k; i++)
{
if(i != k-2)
{
p[k-1][Lcir] = p[k-1][Lcir] + p[i][Lcir+1];
p[k-1][Lcir] = p[k-1][Lcir] % 1000000007;
}
else
continue;
}
}
else
{
for(i=0; i<k; i++)
{
if( (i != Hcir+1) && (i != Hcir-1) )
{
p[Hcir][Lcir] = p[Hcir][Lcir] + p[i][Lcir+1];
p[Hcir][Lcir] = p[Hcir][Lcir] % 1000000007;
}
else
continue;
}
}
}
}
for(i=1; i<k; i++)
{
sum = sum + p[i][0];
sum = sum % 1000000007;
}
printf("%ld", sum);
// for(i=0; i<k; i++)
// {
// for(j=0; j<l; j++)
// {
// printf("%ld ", p[i][j]);
// }
// printf("\n");
// }
// 輸出陣列
return 0;
}