藍橋杯(歷屆真題)——剪格子 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:不要吐槽我變數命名嘛,和大佬學的哈哈哈