一、题目
今有6×6的棋盘,其中某些格子已预放了棋子。现在要再放上去一些,使得每行每列都正好有3颗棋子。最多有多少种放法?
二、数据分析
需要每次都计算行和列,所以需要做好准备工作
int direct_sum(int arr[6][6], int idx, char direction)
数组在某个方向上的和bool check_arr(int arr[6][6])
检查每个行列上是否满足条件
int direct_sum(int arr[6][6], int idx, char direction){
int out = 0;
if(direction == 'r')
for(int i=0; i<6; i++) out += arr[idx][i];
if(direction == 'c')
for(int i=0; i<6; i++) out += arr[i][idx];
return out;
}
bool check_arr(int arr[6][6]){
for(int idx=0; idx<6; idx++){
int row_sum = 0, col_sum = 0;
for(int nd=0; nd<6; nd++){
row_sum += arr[idx][nd];
col_sum += arr[nd][idx];
}
if(row_sum != 3 | col_sum != 3) return false;
}
return true;
}
void observe_1(int arr[6][6]){
for(int r=0; r<6; r++){
int nd = 3 - direct_sum(arr, r, 'r');
cout << "row[ " << r << " ]: need_extra-p: " << nd << endl;
nd = 3 - direct_sum(arr, r, 'c');
cout << "col[ " << r << " ]: need_extra-p: " << nd << endl;
}
}
int main(){
int arr[6][6] = {
{1,0,0,0,0,0}, // 2
{0,0,1,0,1,0}, // 1
{0,0,1,1,0,1}, // 0
{0,1,0,0,1,0}, // 1
{0,0,0,1,0,0}, // 2
{1,0,1,0,0,1} // 0
// 1 2 0 1 1 1
};
observe_1(arr);
return 0;
}
row[ 0 ]: need_extra-p: 2
col[ 0 ]: need_extra-p: 1
row[ 1 ]: need_extra-p: 1
col[ 1 ]: need_extra-p: 2
row[ 2 ]: need_extra-p: 0
col[ 2 ]: need_extra-p: 0
row[ 3 ]: need_extra-p: 1
col[ 3 ]: need_extra-p: 1
row[ 4 ]: need_extra-p: 2
col[ 4 ]: need_extra-p: 1
row[ 5 ]: need_extra-p: 0
col[ 5 ]: need_extra-p: 1
三、解题
因为存在多种填充解,显然可以用递归遍历多个路径。我们可以先用循环写出一个路径,然后再逐步改成递归
3.1 找出其中一种解
- 逐行遍历
- 当行上满足条件的时候直接看下一行
- 当棋盘上已经有棋子,继续行上下一个位置
- 当列上可以添加棋子的时候,添加棋子
int main(){
int arr[6][6] = {
{1,0,0,0,0,0}, // 2
{0,0,1,0,1,0}, // 1
{0,0,1,1,0,1}, // 0
{0,1,0,0,1,0}, // 1
{0,0,0,1,0,0}, // 2
{1,0,1,0,0,1} // 0
// 1 2 0 1 1 1
};
for(int r=0; r<6; r++){
// 逐行遍历
for(int c=0; c<6; c++){
// 当行上满足条件的时候直接看下一行
if(direct_sum(arr, r, 'r') == 3 ) break;
// 当棋盘上已经有棋子,继续
if(arr[r][c]) continue;
// 当列上可以添加棋子的时候,添加
if(direct_sum(arr, c, 'c') < 3){
arr[r][c] = 1;
}
}
}
cout << check_arr(arr) << endl;
observe_1(arr);
return 0;
}
3.2 递归修改
从循环中我们可以得到一个终止条件:
- 行遍历结束 <=>
r == 6
- 在终止的时候我们需要判断下是否满足了我们的需求
几个进入下一个递归的条件:
- 当行上满足条件的时候直接看下一行
direct_sum(arr, r, 'r') == 3
- 当棋盘上已经有棋子直接下步
arr[r][c]
- 当列上满足条件直接下步
direct_sum(arr, c, 'c') == 3
同时我们需要构建一个next_step
直接下一步的辅助递归函数,
从下列的实现我们可以看出基本是和循环一致的,仅仅是将循环的一些跳跃动作break ; continue
用if else
连接。
增加了多种可能性的探索将下棋的点不落子,继续下一步
int N=0;
void final_fill_arr(int arr[6][6], int r, int c);
void next_step(int x[6][6], int r, int c)
{
if( c < 6 )
final_fill_arr(x, r, c+1);
else
final_fill_arr(x, r+1, 0);
};
void final_fill_arr(int arr[6][6], int r, int c){
if(r==6){
if(check_arr(arr)){
N++;
bool show_flag = true;
if(show_flag){
cout << " " << endl;
show_arr(arr);
}
}
return;
}
// 当行上满足条件的时候直接看下一行
if(direct_sum(arr, r, 'r') == 3) final_fill_arr(arr, r + 1, 0);
// 当棋盘上已经有棋子, 看下一步
else if(arr[r][c]) next_step(arr, r, c);
// 当列上可以添加则添加
else if( direct_sum(arr, c, 'c') < 3 ){
arr[r][c] = 1;
next_step(arr, r, c);
// 多种可能遍历
arr[r][c] = 0;
next_step(arr, r, c);
}else{
next_step(arr, r, c);
}
}
四、完整解
# include<iostream>
# include<string>
# include<stdio.h>
using namespace std;
int direct_sum(int arr[6][6], int idx, char direction){
int out = 0;
if(direction == 'r')
for(int i=0; i<6; i++) out += arr[idx][i];
if(direction == 'c')
for(int i=0; i<6; i++) out += arr[i][idx];
return out;
}
bool check_arr(int arr[6][6]){
for(int idx=0; idx<6; idx++){
int row_sum = 0, col_sum = 0;
for(int nd=0; nd<6; nd++){
row_sum += arr[idx][nd];
col_sum += arr[nd][idx];
}
if(row_sum != 3 | col_sum != 3) return false;
}
return true;
}
void observe_1(int arr[6][6]){
for(int r=0; r<6; r++){
int nd = 3 - direct_sum(arr, r, 'r');
cout << "row[ " << r << " ]: need_extra-p: " << nd << endl;
nd = 3 - direct_sum(arr, r, 'c');
cout << "col[ " << r << " ]: need_extra-p: " << nd << endl;
}
}
void show_arr(int arr[6][6]){
{
for(int i=0; i<6; i++)
{
for(int j=0; j<6; j++) printf("%2d", arr[i][j]);
printf("\n");
}
printf("\n");
}
}
int N=0;
void final_fill_arr(int arr[6][6], int r, int c);
void next_step(int x[6][6], int r, int c)
{
if( c < 6 )
final_fill_arr(x, r, c+1);
else
final_fill_arr(x, r+1, 0);
};
void final_fill_arr(int arr[6][6], int r, int c){
if(r==6){
if(check_arr(arr)){
N++;
bool show_flag = true;
if(show_flag){
cout << " " << endl;
show_arr(arr);
}
}
return;
}
// 当行上满足条件的时候直接看下一行
if(direct_sum(arr, r, 'r') == 3) final_fill_arr(arr, r + 1, 0);
// 当棋盘上已经有棋子, 看下一步
else if(arr[r][c]) next_step(arr, r, c);
// 当列上可以添加则添加
else if( direct_sum(arr, c, 'c') < 3 ){
arr[r][c] = 1;
next_step(arr, r, c);
arr[r][c] = 0;
next_step(arr, r, c);
}else{
next_step(arr, r, c);
}
}
int main(){
int arr[6][6] = {
{1,0,0,0,0,0}, // 2
{0,0,1,0,1,0}, // 1
{0,0,1,1,0,1}, // 0
{0,1,0,0,1,0}, // 1
{0,0,0,1,0,0}, // 2
{1,0,1,0,0,1} // 0
// 1 2 0 1 1 1
};
show_arr(arr);
final_fill_arr(arr, 0, 0);
cout << N << endl;
// show_arr(arr);
return 0;
}
输出
./try
1 0 0 0 0 0
0 0 1 0 1 0
0 0 1 1 0 1
0 1 0 0 1 0
0 0 0 1 0 0
1 0 1 0 0 1
1 1 0 1 0 0
1 0 1 0 1 0
0 0 1 1 0 1
0 1 0 0 1 1
0 1 0 1 1 0
1 0 1 0 0 1
1 1 0 1 0 0
0 1 1 0 1 0
0 0 1 1 0 1
1 1 0 0 1 0
0 0 0 1 1 1
1 0 1 0 0 1
1 1 0 1 0 0
0 1 1 0 1 0
0 0 1 1 0 1
0 1 0 0 1 1
1 0 0 1 1 0
1 0 1 0 0 1
1 1 0 1 0 0
0 0 1 0 1 1
0 0 1 1 0 1
1 1 0 0 1 0
0 1 0 1 1 0
1 0 1 0 0 1
1 1 0 0 1 0
1 0 1 0 1 0
0 0 1 1 0 1
0 1 0 1 1 0
0 1 0 1 0 1
1 0 1 0 0 1
1 1 0 0 1 0
0 1 1 0 1 0
0 0 1 1 0 1
0 1 0 1 1 0
1 0 0 1 0 1
1 0 1 0 0 1
1 1 0 0 1 0
0 0 1 1 1 0
0 0 1 1 0 1
1 1 0 0 1 0
0 1 0 1 0 1
1 0 1 0 0 1
1 1 0 0 1 0
0 0 1 1 1 0
0 0 1 1 0 1
0 1 0 0 1 1
1 1 0 1 0 0
1 0 1 0 0 1
1 1 0 0 1 0
0 0 1 0 1 1
0 0 1 1 0 1
0 1 0 1 1 0
1 1 0 1 0 0
1 0 1 0 0 1
1 1 0 0 0 1
1 0 1 0 1 0
0 0 1 1 0 1
0 1 0 1 1 0
0 1 0 1 1 0
1 0 1 0 0 1
1 1 0 0 0 1
0 1 1 0 1 0
0 0 1 1 0 1
0 1 0 1 1 0
1 0 0 1 1 0
1 0 1 0 0 1
1 1 0 0 0 1
0 0 1 1 1 0
0 0 1 1 0 1
1 1 0 0 1 0
0 1 0 1 1 0
1 0 1 0 0 1
1 0 0 1 1 0
0 1 1 0 1 0
0 0 1 1 0 1
1 1 0 0 1 0
0 1 0 1 0 1
1 0 1 0 0 1
1 0 0 1 1 0
0 1 1 0 1 0
0 0 1 1 0 1
0 1 0 0 1 1
1 1 0 1 0 0
1 0 1 0 0 1
1 0 0 1 0 1
0 1 1 0 1 0
0 0 1 1 0 1
1 1 0 0 1 0
0 1 0 1 1 0
1 0 1 0 0 1
1 0 0 0 1 1
0 1 1 0 1 0
0 0 1 1 0 1
0 1 0 1 1 0
1 1 0 1 0 0
1 0 1 0 0 1
16