藍橋杯之格子刷油漆

問題描述
  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



思路:
名詞解釋相對的格子:一列之中,除了指定格子之外的另一個格子。
1、構造兩個動態規劃數組和一個計數器sum,一個數組a[x],表示在2*x的格子條件下,從最邊緣一列的一個角的格子出發,遍歷全體格子的種類數,顯然a[1]=1,另一個數組b[x],表示在2*x的格子條件下,從一個角的格子出發,遍歷全體格子後回到與之相對的格子的種類數。如圖所示,顯然因爲要考慮到回來的路徑,因此除了出發點之外,每一列都只有2種選擇方法,因此b[x]=2*b[x-1]  
2、先考慮出發點在角上的問題,從一個角出發,只有3種可能性,(1)那就是先去相對的格子,然後前往下一列,這就簡化成爲從2*x-1列的格子中,從一個角出發遍歷所有格子的問題,因爲前往下一列的第一個格子有兩種選法,因此a[x]+=2*a[x-1];(2)第二種可能性就是先去遍歷其餘格子,最後以相對的格子收尾。此時a[x]+= b[x];(3)第三種可能性較爲複雜,先經過第二列的一次轉折,然後到第三列的一個角上進行遍歷。此時第二列有2種選法,第三列有2種選法,因此a[x]+=4*a[x-2]; 
3、再去考慮出發點在中間的問題,如圖所示,出發點在中間的時候,顯然不能直接往下走,否則無法遍歷所有點,應當是先遍歷左邊(右邊)所有點,然後回到相對的點,然後遍歷右邊(左邊)的點。注意先遍歷的時候,必須是採用“遍歷全體格子後回到與之相對的格子”的走法,否則無法遍歷出發點正下方的點,而後遍歷則不受限制。因此設從第i列開始出發,出發點有兩種選法,第一落腳點又有兩種走法,後遍歷的第一落腳點又有兩種走法,走完總走法數爲2*(2*b[i-1]*2*a[n-i])+2*(2*b[n-i]*2*a[i-1]) (加法的前一半是先遍歷左邊,後一半是先遍歷右邊) 
4、總走法數就是4*a[i](因爲有4個角)+ 從2到第n-1列所有從中間走法數的和。
下面是代碼vs2015編譯通過。時間複雜度爲o[n]
#include <iostream>
using namespace std;

__int64 a[1001] = { 0 };
__int64 b[1001] = { 0 };
const int NUM = 1000000007;
int main()
{
	ios::sync_with_stdio(false);
	int i, n;
	cin >> n;
	b[1] = 1;
	for (i = 2;i <= n;i++)
		b[i] = (b[i - 1] * 2 % NUM);
	a[1] = 1;a[2] = 6;
	for (i = 3;i <= n;i++)
		a[i] = (2 * a[i - 1] + b[i] + 4 * a[i - 2]) % NUM;
	__int64 sum = 4 * a[n];
	for (i = 2;i<n;i++)
	{
		sum += ((8 * b[n - i] * a[i - 1]) % NUM + (8 * a[n - i] * b[i - 1]) % NUM) % NUM;
		sum %= NUM;
	}
	if (n == 1)  sum = 2;
	cout << sum <<endl;

	return 0;
}

 
方法二:

寫的太好了,啓發了我的思路(之前思路非常不好),我的思路基本上就是照抄他的,但是最後求解過程以及初始狀態有些許不同,再次膜拜。

這位大牛解此題的關鍵就在於創建了兩個數組,設爲q2[n]和q1[n],分別存儲長度爲n的矩陣從一角開始遍歷完後終於任意點以及終於同一列相對位置點的數量

遞推關係有:

       q1[n]=q1[n-1]*2(可以根據定義以及格子關係求得)

       q2[n]=q1[n-1]*2+q2[n-1]*2+q2[n-2]*4(這個式子見下圖以及說明)

情況一 先去同一列相鄰格子 q2[n]+=q2[n-1]*2

情況二 先去相鄰列的同行格子 

         然後又有 先去右下角再來左邊部分 q2[n]+=q2[n-2]*2

                          先遍歷完左邊部分再去左下角 q2[n]+=q1[n-1]

情況三 直接來左下角

         然後又有 先去右邊格子 再遍歷完左邊 q2[n]+=q2[n-2]*2

                          先遍歷完左邊再去右邊的格子 q2[n]+=q1[n-1]

整理就得出上式


最終結果就是(設矩形長度爲n)

四個角作爲起點的數量加上中間開始的數量

從中間某列的一點(2種選擇)出發一定要先遍歷完這一列的一側並且回到這一列的另一個點(這就是q1派上用場的時候),另外一側就直接用q2表示的數目加上好了(兩種選擇),所以是下式中有個4;

4*q2[n]+∑(2到n-1)(4*q1[i]*q2[n-i]+4*q1[n-i+1]*q2[i-1])(兩部分分別爲先遍歷左邊的和先遍歷右邊的)

最後別忘了經常mod1000000007

下面附上奇短的代碼

#include<iostream>
#define DF 1000000007
using namespace std;
long a, b, c, d, n;
long long sum, q1[1001], q2[1001];
int main()
{
	cin >> n;
	q1[1] = 1;
	q2[1] = 1;
	q1[2] = 2;
	q2[2] = 6;
	for (a = 3;a <= n;a++)
	{
		q1[a] = q1[a - 1] * 2;
		q1[a] %= DF;
		q2[a] = q2[a - 1] * 2 + q1[a - 1] * 2 + q2[a - 2] * 4;
		q2[a] %= DF;
	}
	sum = (q2[n] * 4) % DF;
	for (a = 2;a<n;a++)
		sum = (sum + 4 * (q1[a] * q2[n - a] % DF + q1[n - a + 1] * q2[a - 1] % DF)) % DF;
	cout << sum << endl;
}


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