目录
一,数壹
涂黑部分格子,同时满足以下规则:
- 每行及每列中,同一个数字不能出现1次以上。
- 涂黑的格子不能水平相邻或垂直相邻。
- 所有未涂黑的格子应该连成一个整体。
5*5


10*10


策略
从边界开始,先推出一个确定涂黑的格子,然后它的邻居一定是不涂黑的,以此类推。
二,数和
1. 方格内需填入1-9中的一个数
2. 黑色方格内的数提示了与其相邻(右方或下方)的连续白色方格中数的总和。
3. 连续白色方格中的数不得重复。
6*6


10*10



SGU - 454 Kakuro
Kakuro puzzle is played on a nx m grid of "black" and "white" cells. Apart from the top row and leftmost column which are entirely black, the grid has some amount of white cells which form "runs" and some amount of black cells. "Run" is a vertical or horizontal maximal one-lined block of adjacent white cells. Each row and column of the puzzle can contain more than one "run". Every white cell belongs to exactly two runs — one horizontal and one vertical run. Each horizontal "run" always has a number in the black half-cell to its immediate left, and each vertical "run" always has a number in the black half-cell immediately above it. These numbers are located in "black" cells and are called "clues".
The rules of the puzzle are simple:
- place a single digit from 1 to 9 in each "white" cell
- each digit may only be used once in each "run"
- for all runs, the sum of all digits in a "run" must match the clue associated with the "run"
Given the grid, your task is to find a solution for the puzzle.

Picture of the first sample input

Picture of the first sample output
Input
The first line of input contains two integers n and m (2 ≤ n,m ≤ 6) — the number of rows and columns correspondingly. Each of the next n lines contains descriptions of m cells. Each cell description is one of the following 5-character strings:
-
.....
— "white" cell; -
XXXXX
— "black" cell with no clues; -
AA\BB
— "black" cell with one or two clues.AA
is either a 2-digit clue for the corresponding vertical run, orXX
if there is no associated vertical run.BB
is either a 2-digit clue for the corresponding horizontal run, orXX
if there is no associated horizontal run.
The first row and the first column of the grid will never have any white cells. The given grid will have at least one "white" cell.
It is guaranteed that the given puzzle has at least one solution.
Output
Print n lines to the output with m cells in each line. For every "black" cell print '

' (underscore), for every "white" cell print the corresponding digit from the solution. Delimit cells with a single space, so that each row consists of 2m-1 characters.
If there are many solutions, you may output any of them.
Sample 1
| Inputcopy | Outputcopy |
|---|---|
6 6 XXXXX XXXXX 28\XX 17\XX 28\XX XXXXX XXXXX 22\22 ..... ..... ..... 10\XX XX\34 ..... ..... ..... ..... ..... XX\14 ..... ..... 16\13 ..... ..... XX\22 ..... ..... ..... ..... XXXXX XXXXX XX\16 ..... ..... XXXXX XXXXX | _ _ _ _ _ _ _ _ 5 8 9 _ _ 7 6 9 8 4 _ 6 8 _ 7 6 _ 9 2 7 4 _ _ _ 7 9 _ _ |
Sample 2
| Inputcopy | Outputcopy |
|---|---|
3 3 XXXXX 04\XX XXXXX XX\04 ..... XXXXX XXXXX XXXXX XXXXX | _ _ _ _ 4 _ _ _ _ |
#include<iostream>
#include<string>
using namespace std;
int n, m;
int col[6][6], row[6][6], ans[6][6];
int numr[6][6], numc[6][6];
bool pre[6][36][10];//pre[i][j][k]表示是否存在包含k的i个不同的数使得和为j
int flag[6][6][10];//flag[i][j][k]=2才表示根据pre算出来(i,j)处是否可能是k
void getpre()//初始化pre
{
int s;
for (int i = 1; i < 6; i++)for (int j = 1; j < 36; j++)
for (int k = 1; k < 10; k++)pre[i][j][k] = false;
for (int x1 = 1; x1 < 10; x1++)
{
pre[1][x1][x1] = true;
for (int x2 = x1 + 1; x2 < 10; x2++)
{
pre[2][x1 + x2][x1] = pre[2][x1 + x2][x2] = true;
for (int x3 = x2 + 1; x3 < 10; x3++)
{
s = x1 + x2 + x3;
pre[3][s][x1] = pre[3][s][x2] = pre[3][s][x3] = true;
for (int x4 = x3 + 1; x4 < 10; x4++)
{
pre[4][s + x4][x1] = pre[4][s + x4][x2] = true;
pre[4][s + x4][x3] = pre[4][s + x4][x4] = true;
for (int x5 = x4 + 1; x5 < 10; x5++)
{
pre[5][s + x4 + x5][x1] = true;
pre[5][s + x4 + x5][x2] = pre[5][s + x4 + x5][x3] = true;
pre[5][s + x4 + x5][x4] = pre[5][s + x4 + x5][x5] = true;
}
}
}
}
}
}
void getflag()//初始化flag和numr和numc
{
for (int i = 1; i < n; i++)for (int j = 1; j < m; j++)
for (int k = 1; k < 10; k++)flag[i][j][k] = 0;
int l;
for (int i = 0; i < n; i++)for (int j = 0; j < m; j++)
{
if (row[i][j] > 0)
{
l = 0;
while (row[i][j + l + 1] < 0)l++;
numr[i][j] = l;
for (int n = 1; n < 10; n++)if (pre[l][row[i][j]][n])
for (int k = 1; k <= l; k++)flag[i][j + k][n]++;
}
if (col[i][j] > 0)
{
l = 0;
while (col[i + l + 1][j] < 0)l++;
numc[i][j] = l;
for (int n = 1; n < 10; n++)if (pre[l][col[i][j]][n])
for (int k = 1; k <= l; k++)flag[i + k][j][n]++;
}
}
}
int num(char a, char b)
{
if (a == 'X')return 0;
if (a == '.')return -1;
return (a - '0') * 10 + b - '0';
}
bool ok(int r, int c, int k)
{
int i, j, s = k, l=-1;
for (j = c - 1; ans[r][j] > 0; j--)
{
if (ans[r][j] == k)return false;
s += ans[r][j], l--;
}
l += numr[r][j];
if (s > row[r][j] - (l + 1)*l / 2)return false;
if (s < row[r][j] - (19 - l)*l / 2)return false;
//if (c == m - 1 || col[r][c + 1] >= 0)if (s != row[r][j])return false;
s = k, l = -1;
for (i = r - 1; ans[i][c] > 0; i--)
{
if (ans[i][c] == k)return false;
s += ans[i][c], l--;
}
l += numc[i][c];
if (s > col[i][c] - (l + 1)*l / 2)return false;
if (s < col[i][c] - (19 - l)*l / 2)return false;
//if (r == n - 1 || row[r + 1][c] >= 0)if (s != col[i][c])return false;
return true;
}
bool trys(int r, int c)
{
if (c == m)r++, c = 1;
if (r == n)return true;
if (col[r][c] >= 0)return trys(r, c + 1);
if (r % 2)
{
for (int k = 1; k <= 9; k++)if (flag[r][c][k] == 2 && ok(r, c, k))
{
ans[r][c] = k;
if (trys(r, c + 1))return true;
}
}
else
{
for (int k = 9; k >= 1; k--)if (flag[r][c][k] == 2 && ok(r, c, k))
{
ans[r][c] = k;
if (trys(r, c + 1))return true;
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
getpre();
cin >> n >> m;
string s;
for (int i = 0; i < n; i++)for (int j = 0; j < m; j++)
{
cin >> s;
col[i][j] = num(s[0], s[1]);
row[i][j] = num(s[3], s[4]);
ans[i][j] = 0;
}
getflag();
trys(1, 1);
for (int i = 0; i < n; i++)for (int j = 0; j < m; j++)
{
if (ans[i][j])cout << ans[i][j];
else cout << '_';
if (j < m - 1)cout << ' ';
else cout << endl;
}
return 0;
}
没能AC,Time Limit Exceeded on test 218
三,旋转数迷
1,时间复杂度
有(n+1)*(n+1)个数需要排序,如果n=4,那就是25!种情况,数字很大,肯定需要剪枝。
剪枝方法:
- 先选2个数(即(0,0)和(1,0))
- 然后选n个,每选1个,就可以推理出1个(选(0,i)可以推出(1,i),i从1到n)
- 接下来n-1轮,每一轮中,只要选出1个数((i,0),i从2到n),就可以推理出n个数(从(i,1)到(i,n))
所以,一共只需要选2n+1次,除了最前面的2个之外,每选1次都可以做一次剪枝,总时间复杂度应该不会很高。
2,dfs求解
直接用大模型生成:
田字格网格填充问题,输入:整数 n,(n+1)*(n+1) 个正整数 num,n*n 个正整数 s,将 num 填入 (n+1)*(n+1) 的网格,使得每个 2x2 田字格的四个数之和等于对应的 s
// 前向声明
bool backtrack(int pos, int grid_size, vector<vector<int>>& grid,
const vector<int>& num, vector<int>& used,
const vector<vector<int>>& s_2d, int n, vector<vector<int>>& result);
bool isValid(const vector<vector<int>>& grid, const vector<vector<int>>& s_2d,
int row, int col, int n);
/**
* 田字格网格填充问题
* 输入:整数 n,(n+1)*(n+1) 个正整数 num,n*n 个正整数 s
* 将 num 填入 (n+1)*(n+1) 的网格,使得每个 2x2 田字格的四个数之和等于对应的 s
*
* 约束关系:对于位置 (i,j) 的田字格,有:
* grid[i][j] + grid[i][j+1] + grid[i+1][j] + grid[i+1][j+1] = s[i][j]
*/
vector<vector<int>> solve(int n, vector<int> s, vector<int> num) {
int grid_size = n + 1;
int total_cells = grid_size * grid_size;
// 检查输入有效性
if (s.size() != (size_t)n * n || num.size() != (size_t)total_cells) {
return {};
}
// 将 num 排序
sort(num.begin(), num.end());
// 将一维的 s 转为二维
vector<vector<int>> s_2d(n, vector<int>(n));
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
s_2d[i][j] = s[i * n + j];
}
}
vector<vector<int>> result;
vector<int> used(total_cells, 0);
vector<vector<int>> grid(grid_size, vector<int>(grid_size, 0));
if (backtrack(0, grid_size, grid, num, used, s_2d, n, result)) {
return result;
}
return {};
}
/**
* 回溯函数
*/
bool backtrack(int pos, int grid_size, vector<vector<int>>& grid,
const vector<int>& num, vector<int>& used,
const vector<vector<int>>& s_2d, int n, vector<vector<int>>& result) {
int total_cells = grid_size * grid_size;
if (pos == total_cells) {
result = grid;
return true;
}
int row = pos / grid_size;
int col = pos % grid_size;
// 尝试填入未使用的数字
for (int i = 0; i < (int)num.size(); i++) {
if (!used[i]) {
int val = num[i];
used[i] = 1;
grid[row][col] = val;
if (isValid(grid, s_2d, row, col, n)) {
if (backtrack(pos + 1, grid_size, grid, num, used, s_2d, n, result)) {
return true;
}
}
used[i] = 0;
grid[row][col] = 0;
}
}
return false;
}
/**
* 验证:检查所有已完成的田字格约束
* 一个田字格 (i,j) 被认为是"完成的",当且仅当:
* grid[i][j], grid[i][j+1], grid[i+1][j], grid[i+1][j+1] 全部已被填入
*
* 当我们在位置 (row, col) 填入数字后,以下田字格可能完成:
* - (row-1, col-1): 需要 row>=1, col>=1,且这四个位置都已填过
* - (row-1, col): 需要 row>=1, col+1<grid_size,且这四个位置都已填过
* - (row, col-1): 需要 row+1<grid_size, col>=1,且这四个位置都已填过
* - (row, col): 需要 row+1<grid_size, col+1<grid_size,且这四个位置都已填过
*/
bool isValid(const vector<vector<int>>& grid, const vector<vector<int>>& s_2d,
int row, int col, int n) {
int grid_size = n + 1;
// 检查田字格 (row-1, col-1)
if (row >= 1 && col >= 1 &&
grid[row - 1][col - 1] != 0 && grid[row - 1][col] != 0 &&
grid[row][col - 1] != 0) {
int sum = grid[row - 1][col - 1] + grid[row - 1][col] +
grid[row][col - 1] + grid[row][col];
if (sum != s_2d[row - 1][col - 1]) {
return false;
}
}
// 检查田字格 (row-1, col)
if (row >= 1 && col + 1 < grid_size &&
grid[row - 1][col] != 0 && grid[row - 1][col + 1] != 0 &&
grid[row][col + 1] != 0) {
int sum = grid[row - 1][col] + grid[row - 1][col + 1] +
grid[row][col] + grid[row][col + 1];
if (sum != s_2d[row - 1][col]) {
return false;
}
}
// 检查田字格 (row, col-1)
if (row + 1 < grid_size && col >= 1 &&
grid[row + 1][col - 1] != 0 && grid[row + 1][col] != 0 &&
grid[row][col - 1] != 0) {
int sum = grid[row][col - 1] + grid[row][col] +
grid[row + 1][col - 1] + grid[row + 1][col];
if (sum != s_2d[row][col - 1]) {
return false;
}
}
// 检查田字格 (row, col)
if (row + 1 < grid_size && col + 1 < grid_size &&
grid[row][col + 1] != 0 && grid[row + 1][col] != 0 && grid[row + 1][col + 1] != 0) {
int sum = grid[row][col] + grid[row][col + 1] +
grid[row + 1][col] + grid[row + 1][col + 1];
if (sum != s_2d[row][col]) {
return false;
}
}
return true;
}
int main() {
auto ans=solve(2, vector<int>{8,7,9,6}, vector<int>{1, 1, 2, 2, 2, 2,3,3,3});
for (auto v : ans) {
for (auto x : v)cout << x << " ";
cout << endl;
}
return 0;
}
3,运行实例
简单 n=2
auto ans=solve(2, vector<int>{8,7,9,6}, vector<int>{1, 1, 2, 2, 2, 2,3,3,3});
运行结果:
2 2 3
3 1 1
3 2 2

中级 n=2

auto ans=solve(2, vector<int>{16,21,19,22}, vector<int>{6,7,3,4,5,8,2,1,9});
运行结果:
1 5 9
6 4 3
2 7 8

高级 n=3

auto ans = solve(3, vector<int>{51,67,73,77,50,43,89,53,46}, vector<int>{9,27,8,22,28,20,24,23,12,4,14,19,6,21,7,15});
运行结果:
4 12 28 19
14 21 6 20
27 15 8 9
24 23 7 22
困难 n=4

auto ans = solve(4, vector<int>{51,100,124,114,53,49,82,138,97,82,122,130,120,131,133,85},
vector<int>{48,32,15,2,14,19,47,39,21,40,16,27,13,18,42,35,3,7,17,12,30,41,46,22,23});
运行结果:
7 27 47 32 19
14 3 23 22 41
15 21 2 35 40
48 13 46 39 16
17 42 30 18 12

4,操作方法
参考数字拼图 旋转
右下角的6589,要变成5689,公式就是上上左上右上上右右左右右右左右右右
四,视野填充
1,规则
初始局面是一个矩形,里面只有数字和空格,每个空格都需要判断是空地(用点表示)还是墙(用黑色正方形表示)。
需要满足的条件是:
(1)墙的四邻居不能用墙
(2)数字和空地构成的所有格子,是一个连通集
(3)每个数字格,往上下左右四个方向的视野包含的格子总数等于数字格的数字。
PS:视野只被墙和矩形边界截断。
2,实战1








3,实战2




4,策略
优先根据很大或者很小的数开始推理,每次只要推理出1个格子是空地或者墙,就是一轮成功的推理。
反复的一个格子一个格子的推,最终就能全部推出来。
本文介绍了一种解决Kakuro数独游戏的方法,包括游戏规则、解题策略和算法实现。通过预处理可能的数字组合并利用回溯搜索,确保了解的正确性和有效性。
数壹、数和、旋转数迷、视野填充&spm=1001.2101.3001.5002&articleId=126564469&d=1&t=3&u=b0d6e38a71134946bbb8a8e53514222a)

被折叠的 条评论
为什么被折叠?



