如图,如下的10个格子,填入0~9的数字。要求:连续的两个数字不能相邻。(左右、上下、对角都算相邻)一共有多少种可能的填数方案?请填写表示方案数目的整数。
思路
可以线性化这个方格,然后用【全排列函数】next_permutation,给出全排列,然后判断,但是多余情况比较多,而且线性化之后,判断变得麻烦
不如直接模拟这个方格,然后dfs+状态重置
- 如何表示一个未填写的位置呢?用一个很大的数(inf)这样无论怎么填,0 ~ 9都不会与 inf 相邻
- 如何避免边界判断?使用一圈 inf 填充方格
值得注意的是每一行的列数不同,需要单独判断
这样做的优点是在填的时候就剪枝了,省去不必要的情况了
代码
答案 1580
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
#define inf 114 // 大数保证空位不会和0~9相邻
#define iabs(x) ((int)fabs(x))
int a[6][6];
int visited[10];
int cnt = 0;
// 通过差值判断是否相邻
int can(int x, int y, int val)
{
if(iabs(a[x-1][y]-val)>1
&& iabs(a[x+1][y]-val)>1
&& iabs(a[x][y-1]-val)>1
&& iabs(a[x][y+1]-val)>1
&& iabs(a[x-1][y-1]-val)>1
&& iabs(a[x-1][y+1]-val)>1
&& iabs(a[x+1][y-1]-val)>1
&& iabs(a[x+1][y+1]-val)>1)
{
return 1;
}
else
{
return 0;
}
}
// 注意对每一行单独处理
// 填第row行第column列,然后从左到右从上到下填
void dfs(int row, int column)
{
if(row == 1)
{
for(int i=0; i<=9; i++)
{
if(visited[i] == 0 && can(row, column, i)==1)
{
visited[i] = 1;
a[row][column] = i;
if(column == 4)
{
dfs(row+1, 1);
}
else
{
dfs(row, column+1);
}
visited[i] = 0;
a[row][column] = inf;
}
}
}
else if(row == 2)
{
for(int i=0; i<=9; i++)
{
if(visited[i] == 0 && can(row, column, i)==1)
{
visited[i] = 1;
a[row][column] = i;
if(column == 4)
{
dfs(row+1, 1);
}
else
{
dfs(row, column+1);
}
visited[i] = 0;
a[row][column] = inf;
}
}
}
else if(row == 3)
{
for(int i=0; i<=9; i++)
{
if(visited[i] == 0 && can(row, column, i)==1)
{
visited[i] = 1;
a[row][column] = i;
if(column == 3)
{
dfs(row+1, 1);
}
else
{
dfs(row, column+1);
}
visited[i] = 0;
a[row][column] = inf;
}
}
}
else if(row == 4)
{
// 填完了,计数
cnt += 1;
}
}
int main()
{
memset(visited, 0, sizeof(visited));
for(int i=0; i<6; i++)
{
for(int j=0; j<6; j++)
{
a[i][j] = inf;
}
}
dfs(1, 2);
cout<<cnt<<endl;
return 0;
}