藍橋杯歷屆試題 格子刷油漆

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


剛剛學了一點動態規劃的基本概念,就去藍橋杯往年試題裏找動規的題目呀,找是找着了,不會寫。。。。在上網查詢大神的解題思路後,自己照葫蘆畫瓢的寫了出來,更重要的是對動態規劃的思想又理解的更深了一些。
這道題乍一看上去很複雜,感覺有很多很多不同的遍歷方法(事實上也的確如此0。0),但我們逐步分解,也能夠條理清晰的分出類來。首先,我們將所有的遍歷方法分爲從角出發和從邊出發兩種,那麼從角出發的我們又可以細分爲先到對塊(對塊指同一列另一塊)再到下一列遍歷一個(N-1)的城牆,和遍歷整個城牆再回到對塊,以及先到下一列再返回到剛開始的對塊再到現在的對塊再到遍歷一個(N-2)的城牆。一共就這三種情況。那麼從邊出發呢,先往左走就一定是從一個角出發遍歷左邊的城牆回到對塊再從一個角出發遍歷右邊城牆,往右走反之,而不存在先往下走,因爲這樣的話將來無法跨過這一列。
那麼從這個思路里我們找到了什麼子結構呢,兩種:從一個角出發遍歷一個更小的城牆數和從一個角出發遍歷一個更小的城牆數還要回來(設爲Traverse和TraverseBack)。這兩種遍歷方法是不一樣的,還要回來的話,我們只能單向的向前走,並且每次只有兩種選擇,易得TraverseBack[N] = 2*TraverseBack[N-1];而不需要回來的情況則比較複雜,但我們通過之前的分析也能得到相應的遞推關係式:Traverse[N] = 2*Traverse[N-1]+TraverseBack[N]+4*Traverse[N-2];
最後我們要求的總方案數就是四個角出發數目加上從2到N-1列每一列的從邊出發方案數目乘以2(因爲一列有兩個)。

附:由於數據規模太大,int肯定是存不下了,Java有biginteger我就直接用了,方便>_<

代碼如下:

import java.math.BigInteger;
import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int N = in.nextInt();
        BigInteger[] Traverse = new BigInteger[N+1];
        BigInteger[] TraverseBack = new BigInteger[N+1];
        Traverse[1] = new BigInteger("1");
        Traverse[2] = new BigInteger("6");
        TraverseBack[1] = new BigInteger("1");
        TraverseBack[2] = new BigInteger("2");
        BigInteger two = new BigInteger("2");
        BigInteger four = new BigInteger("4");
        for (int i = 3; i<=N; i++)
        {
            TraverseBack[i] = TraverseBack[i-1].multiply(two);
            Traverse[i] = Traverse[i-1].multiply(two).add(TraverseBack[i]).add(Traverse[i-2].multiply(four));
        }
        BigInteger corner = Traverse[N].multiply(four);
        BigInteger middle = new BigInteger("0");
        for (int i = 2; i<=N-1; i++)
        {
            middle = middle.add(TraverseBack[i].multiply(four).multiply(Traverse[N-i]))
                    .add(four.multiply(TraverseBack[N-i+1]).multiply(Traverse[i-1]));
        }
        BigInteger result = corner.add(middle);
        System.out.println(result.mod(new BigInteger("1000000007")));
        in.close();
    }
} 

第一次寫DP的題目,也不知道這屬於什麼難度,反正我覺得好難啊。。。。也讓我感受到了動規算法的巧妙之處,寥寥30餘行代碼就解決了,但裏面需要考慮的真挺多的,繼續加油!

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