1. 程式人生 > >藍橋杯(歷屆真題)——剪格子 dfs

藍橋杯(歷屆真題)——剪格子 dfs

題目描述

問題描述

如下圖所示,3 x 3 的格子中填寫了一些整數。
+–*–+–+
|10* 1|52|
+–**–+
|20|30* 1|
*–+
| 1| 2| 3|
+–+–+–+

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

本題的要求就是請你程式設計判定:對給定的m x n 的格子中的整數,是否可以分割為兩個部分,使得這兩個區域的數字和相等。

如果存在多種解答,請輸出包含左上角格子的那個區域包含的格子的最小數目。

如果無法分割,則輸出 0。

輸入格式

程式先讀入兩個整數 m n 用空格分割 (m,n<10)。

表示表格的寬度和高度。

接下來是n行,每行m個正整數,用空格分開。每個整數不大於10000。

輸出格式

輸出一個整數,表示在所有解中,包含左上角的分割區可能包含的最小的格子數目。

樣例輸入1

3 3
10 1 52
20 30 1
1 2 3

樣例輸出1

3

樣例輸入2

4 3
1 1 1 1
1 30 80 2
1 1 1 100

樣例輸出2

10

思考過程

拿到這個題目就想到了用dfs,以左上角的點為起始點開始,遍歷過的點權值求和,如果小於總權值一半兒,就繼續遍歷,否則就return。然後把遍歷點總數最小的情況儲存下來,等遍歷完以後輸出就行。

然後第一次寫的時候第一個資料通過了。但是第二個資料是4*3矩陣邊上一圈加起來十個數佔一半,中間兩個數佔一半的。這樣的情況我最初規定路徑向下走,不然向右走的設定就不能滿足這樣。
在其中也想過是不是用廣度優先更好些。但是後面突然get了if的過程實際上就是設定嘗試行走路線的過程,所以又添加了兩個路徑。判斷過程總的來說就是向下走,不行了就向右走,再不行就向上走,最後還不行就向左走。這樣就可以遍歷到圍圈圈的情況。
因為在這裡用return會出現跳不出去的情況,遞迴就讓人很暈,,所以在這裡我們不強行結束遞迴的值。而是讓他遞迴完成跳出。這樣如果出現兩個滿足條件的情況就很尷尬,所以就設定了兩個全域性變數。使其把遍歷點個數最小的情況儲存下來,這樣就可以不用遞迴跳出。或者就採用flag標誌位的方法也可直接跳出。

程式碼表示

/*
    0v0 AUTHER: 0_Re5et
*/

#include <stdio.h>
#include <stdlib.h>
#define MAX 99999999

int m, n;                 // 存放邊長資訊 
int **p, **visited;       // p存放權值, visited存放點的遍歷資訊 
int boom=0, haha=0;       // boom是現在遍歷過的點權值之和,haha是陣列總和的一半兒 
int Num=0, NUM=MAX;       // Num存放本次boom和haha相等時的點的個數,NUM存放Num的最小值 
void dfs(int x, int y) { int i, j; // 如果座標超出陣列範圍,返回 if(x >= m || y >= n) { return; } // 將此次遍歷到的點權值加入boom,並且標記為置1 boom = boom + p[x][y]; visited[x][y] = 1; // 輸出visited陣列檢視遍歷情況 // printf("now: %d, %d boom: %d\n", x, y, boom); // for(i=0; i<m; i++) // { // for(j=0; j<n; j++) // { // printf("%d ", visited[i][j]); // } // printf("\n"); // } // printf("\n"); // system("pause"); // 當boom值和haha值相等時,記錄Num,並更新NUM if(boom == haha) { for(i=0; i<m; i++) { for(j=0; j<n; j++) { if(visited[i][j] == 1) { Num++; } } } if(Num < NUM) { NUM = Num; } } // 規定深度遍歷路徑 while(x < m && y < n) { // 當陣列沒有超限並且點沒有訪問過並且權值小於haha的時候,先向下走 if((x+1 < m && y < n) && visited[x+1][y] == 0 && boom < haha) { dfs(x+1, y); } // 再向右走 if((x < m && y+1 < n) && visited[x][y+1] == 0 && boom < haha) { dfs(x, y+1); } // 再向上走 if((x-1 >= 0 && y >= 0) && visited[x-1][y] == 0 && boom < haha) { dfs(x-1, y); } // 再向左走 if((x >= 0 && y-1 >= 0) && visited[x][y-1] == 0 && boom < haha) { dfs(x, y-1); } // 上述路徑走完之後,講本點的標誌位置零,權值從boom中減去,並返回 visited[x][y] = 0; boom = boom - p[x][y]; return; } } int main() { int i, j; scanf("%d %d", &n, &m); // 初始化p陣列空間並賦值 p = (int **)malloc(sizeof(int *) * m); for(i=0; i<m; i++) { p[i] = (int *)malloc(sizeof(int) * n); } for(i=0; i<m; i++) { for(j=0; j<n; j++) { scanf("%d", &p[i][j]); } } // 初始化visited陣列空間 visited = (int **)malloc(sizeof(int *) * m); for(i=0; i<m; i++) { visited[i] = (int *)malloc(sizeof(int) * n); } for(i=0; i<m; i++) { for(j=0; j<n; j++) { visited[i][j] = 0; } } // 計算haha的值 for(i=0; i<m; i++) { for(j=0; j<n; j++) { haha = haha + p[i][j]; } } haha = haha/ 2; dfs(0, 0); // 輸出最小數量。如果不能分割,輸出0 if(NUM == MAX) { NUM = 0; } printf("%d", NUM); return 0; }

ps:不要吐槽我變數命名嘛,和大佬學的哈哈哈