天天看點

曆屆試題 格子刷油漆

曆屆試題 格子刷油漆  

問題描述

  X國的一段古城牆的頂端可以看成 2*N個格子組成的矩形(如下圖所示),現需要把這些格子刷上保護漆。

曆屆試題 格子刷油漆

  你可以從任意一個格子刷起,刷完一格,可以移動到和它相鄰的格子(對角相鄰也算數),但不能移動到較遠的格子(因為油漆未幹不能踩!)

  比如:a d b c e f 就是合格的刷漆順序。

  c e f d a b 是另一種合适的方案。

  當已知 N 時,求總的方案數。當N較大時,結果會迅速增大,請把結果對 1000000007 (十億零七) 取模。

輸入格式

  輸入資料為一個正整數(不大于1000)

輸出格式

  輸出資料為一個正整數。

樣例輸入

2

樣例輸出

24

樣例輸入

3

樣例輸出

96

樣例輸入

22

樣例輸出

359635897

思路:畢竟是決賽題,難度是很大的(對我來說),看了這位部落客的思路,才發現規律真的很奇妙,導航門https://www.jianshu.com/p/214d09476203

1構造數組a[x]、b[x]。a[x]表示在2 * x的格子條件下,從四個角任意一個角的格子出發,周遊全體格子的方法總數,b[x]表示在2 * x的條件下,從四個角任意一個角的格子出發,周遊全體格子後回到與之相對的格子中的方法總數。

顯然,a[1]=1,b[1]=1。

遞推式:

a[x]=

b[x]=b[x-1] * 2

推導如下:

曆屆試題 格子刷油漆
  1. 先考慮出發點在角上,從一個角出發,隻有3種可能性。

    (1)先去同一列相鄰的格子,然後前往下一列,這就簡化成從2 * (x-1)的格子中,從一個角出發,周遊全體格子的 問題。因為前往下一列有兩種選法,是以有2 * a[x-1]種方法。

    (2)先去相鄰列的同一行的格子。又分為:

    先去左下角,再去右邊部分, a[x-2] * 2種方法

    先周遊右邊的所有,再回來左下角,b[x-1]種方法

    (3)先去右下角的格子,又分為:

    先去左邊格子,再周遊右邊,a[x-2] * 2種方法

    先周遊右邊,再去左邊,b[x-1]種方法

曆屆試題 格子刷油漆
曆屆試題 格子刷油漆

整理可得

a[x]=a[x-1] * 2+a[x-2] * 4+b[x-1] * 2

b[x]=2 * b[x-1]

而從中間某列的一點(2種選擇)出發時,顯然不能直接往下走,否則無法周遊所有的點,應當先周遊左邊(右邊)左右的點,然後回到相對的點,再周遊右邊(左邊)的點。假設從第i列出發,出發的點有兩種選擇,第二步也有兩種選擇,是以所有的走法有2 * (b[i-1] * 2 * 2 * a[n-i]+ 2 * b[n-i] * 2 * a[i-1])種。加法的前一半時先周遊左邊,後一半是先周遊右邊。

于是,總數就是4 * a[x]+Σ(2到n-1)2 * (b[i-1] * 2 * 2 * a[n-i]+ 2 * b[n-i] * 2 * a[i-1])

代碼方面注意對取餘的處理就行了

1 #include<bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     int x;
 8     cin >> x;
 9     long  long int a[x+1];
10     long long int b[x+1];
11     a[1]=1;
12     b[1]=1;
13     a[2]=6;
14     long long int def=1000000007;
15     for(int i=2;i<=x;i++){
16         b[i]=(b[i-1]*2);
17         b[i]%=def;
18     }
19     for(int i=3;i<=x;i++){
20         a[i]=(a[i-1]*2+a[i-2]*4+b[i-1]*2);
21         a[i]%=def;
22     }
23     long long int sum=0;
24     sum=4*a[x];
25     for(int i=2;i<=x-1;i++){
26         sum+=2*(b[i-1]*2*2*a[x-i]%def+2*b[x-i]*2*a[i-1]%def);
27         sum%=def;
28     }
29 if(x==1) sum=2;
30     cout <<sum;
31     return 0;
32 }