藍橋杯 剪格子

CoolStar,Cool start發表於2021-01-03

前言

半個月前,自己做藍橋2013年A組真題的時候,做到了剪格子這個題,由於對DFS不熟練,無從下手,自己花了接近一週的時間去重新學習遞迴和DFS,然後今天在看到這個題,就很簡單了。

正文

附上題目:
標題:剪格子

如圖所示,3 x 3 的格子中填寫了一些整數。

我們沿著圖中的紅色線剪開,得到兩個部分,每個部分的數字和都是60。

本題的要求就是請你程式設計判定:對給定的m x n 的格子中的整數,是否可以分割為兩個部分,使得這兩個區域的數字和相等。
如果存在多種解答,請輸出包含左上角格子的那個區域包含的格子的最小數目。
如果無法分割,則輸出 0

程式輸入輸出格式要求:
程式先讀入兩個整數 m n 用空格分割 (m,n<10)
表示表格的寬度和高度
接下來是n行,每行m個正整數,用空格分開。每個整數不大於10000
程式輸出:在所有解中,包含左上角的分割區可能包含的最小的格子數目。

例如:
使用者輸入:
3 3
10 1 52
20 30 1
1 2 3

則程式輸出:
3

再例如:
使用者輸入:
4 3
1 1 1 1
1 30 80 2
1 1 1 100

則程式輸出:
10

如下圖:

在這裡插入圖片描述
在這裡插入圖片描述

資源約定:
峰值記憶體消耗 < 64M
CPU消耗 < 5000ms

請嚴格按要求輸出,不要畫蛇添足地列印類似:“請您輸入…” 的多餘內容。

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。

注意: main函式需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要呼叫依賴於編譯環境或作業系統的特殊函式。
注意: 所有依賴的函式必須明確地在原始檔中 #include , 不能通過工程設定而省略常用標頭檔案。

提交時,注意選擇所期望的編譯器型別。

分析

1.這個題在輸入的時候挖了一個坑,把n,m的順序顛倒了一下,這裡要注意。
2.沿著紅線剪開,其實就是把連續的格子裡面的數值累加起來,適合用DFS進行搜尋。
3.既然說如果有多種答案時,選擇包含第一個單元格的解法,那麼我們可以直接選擇第一個單元格進行DFS。
4.當單元格的數值累加大於表格內所有單元格之和時,我們就可以直接跳出當前遞迴,因此,我們可以提前把表格內所有單元格之和計算出來。

話不多說,直接附上程式碼:

package JA2013;

import java.util.Scanner;

public class _09剪格子 {
    static int n ;
    static int m;
    //table 表示整個表格
    static int [][] table;

    //記錄解法最少格子的數目
    static int minCnt = Integer.MAX_VALUE;

    //記錄表格的總和
    static int sum;

    //記錄單元格是否被訪問
    static int [][] isVi;

    //記錄有多少個解法
    static int ansCnt;
    public static void main(String[] args) {
        //變數初始化
        Scanner sc = new Scanner(System.in);
        m = sc.nextInt();
        n = sc.nextInt();

        table = new int[n][m];
        isVi = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                table[i][j] = sc.nextInt();
                sum += table[i][j];
            }
        }
        //進行DFS
        dfs(0,0,0,0);
        //如果ansCnt等於0則表示無解,按照題目要求輸出0。
        if (ansCnt!=0) {
            System.out.println(minCnt);
        }else {
            System.out.println(ansCnt);
        }

    }

    /**
     *
     * @param i i陣列下標
     * @param j j陣列下標
     * @param s 當前單元格數值累加值
     * @param cnt 當前單元格計數器
     */
    private static void dfs(int i, int j, int s,int cnt) {
        //如果當前陣列下標越界
        //如果單元格的數值累加大於表格內所有單元格之和
        //如果當前單元格已訪問
        if (i < 0 || j < 0 || i == n || j == m ||sum/2 < s ||isVi[i][j] == 1) {
            return;
        }

        //如果當前單元格數值累加值等於表格內所有單元格之和時,表示找到一種解法
        if (sum/2 == s) {
            //記錄
            minCnt = Math.min(minCnt,cnt);
            ansCnt++;
            return;
        }
        //標記已訪問當前單元格
        isVi[i][j] = 1;
        //分別右,下,左,上進行搜尋
        dfs(i,j+1,table[i][j]+s,cnt+1);
        dfs(i+1,j,table[i][j]+s,cnt+1);
        dfs(i,j-1,table[i][j]+s,cnt+1);
        dfs(i-1,j,table[i][j]+s,cnt+1);
        //搜尋完成,進行回溯
        isVi[i][j] = 0;
    }
}


總結

這是一個經典的DFS題目,藍橋杯考遞迴和DFS非常頻繁,所以這個題值得去嘗試。