蓝桥杯历届试题 格子刷油漆

问题描述
  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余行代码就解决了,但里面需要考虑的真挺多的,继续加油!

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