前文的精确覆盖矩形问题C程序在求解较大的矩形时效率较低,参考早先Javascript程序的经验,让mimo
编写 shuffle数组函数,打乱放置顺序以获得随机解。然后用打乱后的数组构建舞蹈链数据结构,搜索时,收集该列的所有行,打乱行顺序以获得随机解,只编写此函数,以及调用此函数语句,并告知插入的位置,不做别的
第一次给出的程序有错,继续提示
修改后程序执行时内存出错,请检查修改,不做别的,
他查出“而内存出错的原因在于 shuffle_ints 的调用方式:将 Node** 强制转换为 int* 进行洗牌是错误的(在 64 位系统上指针大小不等于 int,会破坏内存)”,并且修复了。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// 1. 在 main 开头添加
#include <time.h>
// ---------- 数据结构定义 ----------
typedef struct Point {
int x, y;
} Point;
typedef struct {
char letter;
int count;
} ShapeItem; // 输入的形状项
typedef struct {
char letter;
Point *offsets; // 基础偏移(某个朝向)
int num_cells;
} ShapeBase; // 形状库基础定义
typedef struct {
Point *points; // 规范化后的点集(平移至原点)
int num_points;
int width, height;
} Rotation; // 一个旋转后的形状
typedef struct {
char letter;
Rotation *rotations;
int num_rotations;
} ShapeInfo; // 形状的完整旋转信息
// DLX 节点结构
typedef struct Node {
struct Node *left, *right, *up, *down;
struct Node *col; // 指向列头节点
int rowid; // 行ID,-1 表示列头
int size; // 仅列头有效:该列节点数
} Node;
// ---------- 全局变量 ----------
int W, H;
int total_cols;
ShapeItem *input_shapes;
int input_shape_count;
ShapeInfo *shapes_info;
int *shape_remaining; // 每种形状剩余数量
int *row_to_shape; // 行ID -> 形状索引
Node **row_first_nodes; // 行ID -> 该行任意节点
int total_rows;
int *solution_rows; // 存储解的行ID
int depth; // 当前搜索深度
Node *header; // DLX 头节点
Node *col_headers; // 列头数组
// ---------- 形状库基础定义 ----------
ShapeBase shape_bases[] = {
{'A', (Point[]){{0,0}}, 1},
{'B', (Point[]){{0,0},{1,0}}, 2},
// 可在此添加更多形状,例如:
{'C', (Point[]){{0,0},{1,0},{2,0}}, 3},
// {'D', (Point[]){{0,0},{1,0},{0,1},{1,1}}, 4},
// {'E', (Point[]){{0,0},{1,0},{0,1}}, 3}, // L 形
{'I', (Point[]){{0,0},{1,0},{2,0},{3,0}}, 4},
{'J', (Point[]){{0,0},{0,1},{1,1},{2,1}}, 4},
{'L', (Point[]){{2,0},{0,1},{1,1},{2,1}}, 4},
{'O', (Point[]){{0,0},{1,0},{0,1},{1,1}}, 4},
{'S', (Point[]){{1,0},{2,0},{0,1},{1,1}}, 4},
{'T', (Point[]){{1,0},{0,1},{1,1},{2,1}}, 4},
{'Z', (Point[]){{0,0},{1,0},{1,1},{2,1}}, 4},
{'U', (Point[]){{0,0},{2,0},{0,1},{1,1},{2,1}}, 5}, // 2x3 矩形 (0,0)-(2,1),挖去 (1,0)
{'V', (Point[]){{1,0},{0,1},{1,1}}, 3}, // 2x2 矩形 (0,0)-(1,1),挖去 (0,0)
{'X', (Point[]){{1,0},{0,1},{1,1},{2,1},{1,2}}, 5},//T形加上顶部1块
{0, NULL, 0}
};
// 2. 替换 search 函数为以下代码
// 交换整数
void swap_int(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
// Fisher-Yates 洗牌
void shuffle_ints(int *array, int n) {
for (int i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
swap_int(&array[i], &array[j]);
}
}
// ---------- 辅助函数 ----------
ShapeBase *find_shape_base(char letter) {
for (int i = 0; shape_bases[i].letter != 0; i++) {
if (shape_bases[i].letter == letter)
return &shape_bases[i];
}
return NULL;
}
// 旋转点集
void rotate_points(Point *in, int n, int rot, Point *out) {
for (int i = 0; i < n; i++) {
int x = in[i].x, y = in[i].y;
switch (rot) {
case 0: out[i].x = x; out[i].y = y; break; // 0°
case 1: out[i].x = -y; out[i].y = x; break; // 90°
case 2: out[i].x = -x; out[i].y = -y; break; // 180°
case 3: out[i].x = y; out[i].y = -x; break; // 270°
}
}
}
// 规范化点集(平移使 min_x = min_y = 0),并返回尺寸
void normalize_points(Point *pts, int n, int *min_x, int *min_y, int *max_x, int *max_y) {
int mx = pts[0].x, my = pts[0].y;
int Mx = pts[0].x, My = pts[0].y;
for (int i = 1; i < n; i++) {
if (pts[i].x < mx) mx = pts[i].x;
if (pts[i].y < my) my = pts[i].y;
if (pts[i].x > Mx) Mx = pts[i].x;
if (pts[i].y > My) My = pts[i].y;
}
for (int i = 0; i < n; i++) {
pts[i].x -= mx;
pts[i].y -= my;
}
*min_x = 0; *min_y = 0;
*max_x = Mx - mx;
*max_y = My - my;
}
// 点集排序(简单冒泡,点数极少)
void sort_points(Point *pts, int n) {
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (pts[j].x > pts[j+1].x || (pts[j].x == pts[j+1].x && pts[j].y > pts[j+1].y)) {
Point tmp = pts[j];
pts[j] = pts[j+1];
pts[j+1] = tmp;
}
}
}
}
// 比较两个点集是否相同(假设已排序)
bool points_equal(Point *a, Point *b, int n) {
for (int i = 0; i < n; i++)
if (a[i].x != b[i].x || a[i].y != b[i].y) return false;
return true;
}
// 生成形状的旋转信息
ShapeInfo generate_shape_info(ShapeBase *base) {
ShapeInfo info;
info.letter = base->letter;
info.num_rotations = 0;
info.rotations = malloc(4 * sizeof(Rotation)); // 最多4个旋转
for (int rot = 0; rot < 4; rot++) {
Point temp_pts[4]; // 最多4格
rotate_points(base->offsets, base->num_cells, rot, temp_pts);
// 规范化
int min_x, min_y, max_x, max_y;
normalize_points(temp_pts, base->num_cells, &min_x, &min_y, &max_x, &max_y);
// 排序
sort_points(temp_pts, base->num_cells);
// 检查是否重复
bool duplicate = false;
for (int i = 0; i < info.num_rotations; i++) {
if (info.rotations[i].num_points == base->num_cells &&
points_equal(info.rotations[i].points, temp_pts, base->num_cells)) {
duplicate = true;
break;
}
}
if (!duplicate) {
Point *pts = malloc(base->num_cells * sizeof(Point));
memcpy(pts, temp_pts, base->num_cells * sizeof(Point));
Rotation *r = &info.rotations[info.num_rotations];
r->points = pts;
r->num_points = base->num_cells;
r->width = max_x - min_x + 1; // min_x = 0
r->height = max_y - min_y + 1;
info.num_rotations++;
}
}
return info;
}
// 释放 ShapeInfo
void free_shape_info(ShapeInfo *info) {
for (int i = 0; i < info->num_rotations; i++)
free(info->rotations[i].points);
free(info->rotations);
}
// ---------- DLX 操作 ----------
void cover(Node *col) {
col->left->right = col->right;
col->right->left = col->left;
Node *i = col->down;
while (i != col) {
Node *j = i->right;
while (j != i) {
j->down->up = j->up;
j->up->down = j->down;
j->col->size--;
j = j->right;
}
i = i->down;
}
}
void uncover(Node *col) {
Node *i = col->up;
while (i != col) {
Node *j = i->left;
while (j != i) {
j->col->size++;
j->down->up = j;
j->up->down = j;
j = j->left;
}
i = i->up;
}
col->left->right = col;
col->right->left = col;
}
// 3. 修正后的 shuffle 函数:专门为 Node** 数组编写
void shuffle_nodes(Node **array, int n) {
for (int i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
swap_int((int*)&array[i], (int*)&array[j]); // 安全地交换指针值
}
}
// 4. 修正后的 search_random 函数
bool search_random(Node *header_node) {
if (header_node->right == header_node) return true;
// 选择最小 size 的列
Node *col = header_node->right;
Node *best = col;
int min_size = col->size;
col = col->right;
while (col != header_node) {
if (col->size < min_size) {
min_size = col->size;
best = col;
if (min_size == 0) break;
}
col = col->right;
}
if (min_size == 0) return false;
cover(best);
// 收集行
Node **rows_array = malloc(min_size * sizeof(Node*));
Node *r = best->down;
int count = 0;
while (r != best) {
rows_array[count++] = r;
r = r->down;
}
// 打乱行
shuffle_nodes(rows_array, min_size);
// 遍历尝试
for (int i = 0; i < min_size; i++) {
r = rows_array[i];
int shape_idx = row_to_shape[r->rowid];
if (shape_remaining[shape_idx] > 0) {
// 1. Cover all columns in this row
Node *j = r->right;
while (j != r) {
cover(j->col);
j = j->right;
}
shape_remaining[shape_idx]--;
solution_rows[depth++] = r->rowid;
if (search_random(header_node)) {
free(rows_array);
return true;
}
// 2. Backtrack: Uncover columns
depth--;
shape_remaining[shape_idx]++;
// 必须从右向左遍历 uncover,且需要再次遍历 r 的列
// 因为没有 left 指针,我们先收集这些列,或者再次遍历 r
// 这里再次遍历 r 收集列,然后逆序 uncover
Node **cols_to_uncover = malloc(min_size * sizeof(Node*));
int c_count = 0;
Node *temp = r->right;
while (temp != r) {
cols_to_uncover[c_count++] = temp->col;
temp = temp->right;
}
for (int k = c_count - 1; k >= 0; k--) {
uncover(cols_to_uncover[k]);
}
free(cols_to_uncover);
}
}
free(rows_array);
uncover(best);
return false;
}
bool search(Node *header_node) {return search_random(header_node);}
// ---------- 主函数 ----------
int main() {
char input[1000];
// 在 main 函数内部开头添加
srand(time(NULL));
if (fgets(input, sizeof(input), stdin) == NULL) {
fprintf(stderr, "No input\n");
return 1;
}
// 移除换行符
input[strcspn(input, "\n")] = 0;
// 移除所有空格(简化解析)
char *src = input, *dst = input;
while (*src) {
if (*src != ' ') *dst++ = *src;
src++;
}
*dst = 0;
// 解析宽度和高度
char *p = strchr(input, '[');
if (!p) { fprintf(stderr, "Invalid input format\n"); return 1; }
char head[100];
strncpy(head, input, p - input);
head[p - input] = '\0';
if (sscanf(head, "%d,%d", &W, &H) != 2) {
fprintf(stderr, "Parse error: width and height\n");
return 1;
}
// 解析形状列表
char *q = strrchr(input, ']');
if (!q) { fprintf(stderr, "Missing ']'\n"); return 1; }
char inside[500];
strncpy(inside, p + 1, q - p - 1);
inside[q - p - 1] = '\0';
input_shapes = malloc(10 * sizeof(ShapeItem)); // 假设最多10种形状
input_shape_count = 0;
char *token = strtok(inside, ",");
while (token) {
char letter = token[0];
token = strtok(NULL, ",");
if (!token) { fprintf(stderr, "Missing count for %c\n", letter); return 1; }
int count = atoi(token);
input_shapes[input_shape_count].letter = letter;
input_shapes[input_shape_count].count = count;
input_shape_count++;
token = strtok(NULL, ",");
}
// 为每个输入形状生成旋转信息
shapes_info = malloc(input_shape_count * sizeof(ShapeInfo));
for (int i = 0; i < input_shape_count; i++) {
char letter = input_shapes[i].letter;
ShapeBase *base = find_shape_base(letter);
if (!base) {
fprintf(stderr, "Unsupported shape letter: %c\n", letter);
return 1;
}
shapes_info[i] = generate_shape_info(base);
}
// 计算总放置数 (dry run)
total_rows = 0;
for (int i = 0; i < input_shape_count; i++) {
ShapeInfo *info = &shapes_info[i];
for (int r = 0; r < info->num_rotations; r++) {
Rotation *rot = &info->rotations[r];
int px = W - rot->width + 1;
int py = H - rot->height + 1;
if (px > 0 && py > 0)
total_rows += px * py;
}
}
if (total_rows == 0) {
printf("No solution\n");
return 0;
}
// 分配数组
row_to_shape = malloc(total_rows * sizeof(int));
row_first_nodes = malloc(total_rows * sizeof(Node*));
solution_rows = malloc(total_rows * sizeof(int));
// 构建列头
total_cols = W * H;
col_headers = malloc(total_cols * sizeof(Node));
header = malloc(sizeof(Node));
// 初始化 header
header->right = header;
header->left = header;
header->up = header;
header->down = header;
header->rowid = -1;
header->size = 0;
// 初始化列头并连接
for (int i = 0; i < total_cols; i++) {
Node *c = &col_headers[i];
c->left = c;
c->right = c;
c->up = c;
c->down = c;
c->col = c;
c->rowid = -1;
c->size = 0;
// 插入 header 左侧链表
c->right = header;
c->left = header->left;
header->left->right = c;
header->left = c;
}
// 构建节点(真实构建)
int current_row_id = 0;
for (int idx = 0; idx < input_shape_count; idx++) {
ShapeInfo *info = &shapes_info[idx];
for (int r = 0; r < info->num_rotations; r++) {
Rotation *rot = &info->rotations[r];
int px = W - rot->width + 1;
int py = H - rot->height + 1;
if (px <= 0 || py <= 0) continue;
for (int i = 0; i < px; i++) {
for (int j = 0; j < py; j++) {
Node *row_start = NULL;
for (int p = 0; p < rot->num_points; p++) {
int x = i + rot->points[p].x;
int y = j + rot->points[p].y;
int col_idx = y * W + x; // 列索引
Node *node = malloc(sizeof(Node));
node->rowid = current_row_id;
node->col = &col_headers[col_idx];
// 插入列链表底部
node->up = node->col->up;
node->down = node->col;
node->col->up->down = node;
node->col->up = node;
node->col->size++;
// 行链表
if (row_start == NULL) {
row_start = node;
node->left = node->right = node;
} else {
node->right = row_start;
node->left = row_start->left;
row_start->left->right = node;
row_start->left = node;
}
}
row_to_shape[current_row_id] = idx;
row_first_nodes[current_row_id] = row_start;
current_row_id++;
}
}
}
}
// 初始化形状剩余数量
shape_remaining = malloc(input_shape_count * sizeof(int));
for (int i = 0; i < input_shape_count; i++)
shape_remaining[i] = input_shapes[i].count;
depth = 0;
// 执行搜索
bool found = search(header);
if (found) {
// 创建网格
char **grid = malloc(H * sizeof(char*));
for (int y = 0; y < H; y++) {
grid[y] = malloc(W + 1);
for (int x = 0; x < W; x++)
grid[y][x] = '?';
grid[y][W] = '\0';
}
// 填充网格
for (int i = 0; i < depth; i++) {
int rowid = solution_rows[i];
int shape_idx = row_to_shape[rowid];
char letter = shapes_info[shape_idx].letter;
Node *node = row_first_nodes[rowid];
Node *start = node;
Node *cur = start;
do {
int col_idx = cur->col - col_headers;
int x = col_idx % W;
int y = col_idx / W;
grid[y][x] = letter;
cur = cur->right;
} while (cur != start);
}
// 输出
for (int y = 0; y < H; y++) {
for (int x = 0; x < W; x++) {
printf("%c", grid[y][x]);
if (x < W - 1) printf(" ");
}
printf("\n");
free(grid[y]);
}
free(grid);
} else {
printf("No solution\n");
}
// 释放内存(略,程序结束可不释放)
// 清理形状信息
for (int i = 0; i < input_shape_count; i++)
free_shape_info(&shapes_info[i]);
free(shapes_info);
free(input_shapes);
free(shape_remaining);
free(row_to_shape);
free(row_first_nodes);
free(solution_rows);
free(col_headers);
free(header);
return 0;
}
以上程序只在搜索过程中随机打乱,而JavaScript版本在建立舞蹈链时就打乱了,继续提示
这版可以解出较大的问题了,还是比较慢。再实现打乱放置顺序,然后用打乱后的数组构建舞蹈链数据结构,不做别的
他没有给出具体代码,而是输出了一堆指令
为了实现“先生成并打乱所有放置,再构建舞蹈链”,我们需要将原程序中生成放置和插入节点的逻辑拆分。
你需要做以下修改:
1.修改 Node 结构体:添加 left 指针,以便后续方便地遍历整行。
2.编写 insert_row 函数:将一个已存在的行节点插入到对应的列中。
3.重构 build 函数:使用数组暂存生成的行,打乱后逐个插入。
看起来他不记得自己输出的代码了,比如明明Node 结构体中本来就有 left,他却说要添加,只好把原来的代码复制了
你的原代码中build部分代码如下,请改为打乱后bulld逻辑。
这下输出了正确的代码,替换原代码后,一遍过。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// 1. 在 main 开头添加
#include <time.h>
// ---------- 数据结构定义 ----------
typedef struct Point {
int x, y;
} Point;
typedef struct {
char letter;
int count;
} ShapeItem; // 输入的形状项
typedef struct {
char letter;
Point *offsets; // 基础偏移(某个朝向)
int num_cells;
} ShapeBase; // 形状库基础定义
typedef struct {
Point *points; // 规范化后的点集(平移至原点)
int num_points;
int width, height;
} Rotation; // 一个旋转后的形状
typedef struct {
char letter;
Rotation *rotations;
int num_rotations;
} ShapeInfo; // 形状的完整旋转信息
// DLX 节点结构
typedef struct Node {
struct Node *left, *right, *up, *down;
struct Node *col; // 指向列头节点
int rowid; // 行ID,-1 表示列头
int size; // 仅列头有效:该列节点数
} Node;
// ---------- 全局变量 ----------
int W, H;
int total_cols;
ShapeItem *input_shapes;
int input_shape_count;
ShapeInfo *shapes_info;
int *shape_remaining; // 每种形状剩余数量
int *row_to_shape; // 行ID -> 形状索引
Node **row_first_nodes; // 行ID -> 该行任意节点
int total_rows;
int *solution_rows; // 存储解的行ID
int depth; // 当前搜索深度
Node *header; // DLX 头节点
Node *col_headers; // 列头数组
// ---------- 形状库基础定义 ----------
ShapeBase shape_bases[] = {
{'A', (Point[]){{0,0}}, 1},
{'B', (Point[]){{0,0},{1,0}}, 2},
// 可在此添加更多形状,例如:
{'C', (Point[]){{0,0},{1,0},{2,0}}, 3},
// {'D', (Point[]){{0,0},{1,0},{0,1},{1,1}}, 4},
// {'E', (Point[]){{0,0},{1,0},{0,1}}, 3}, // L 形
{'I', (Point[]){{0,0},{1,0},{2,0},{3,0}}, 4},
{'J', (Point[]){{0,0},{0,1},{1,1},{2,1}}, 4},
{'L', (Point[]){{2,0},{0,1},{1,1},{2,1}}, 4},
{'O', (Point[]){{0,0},{1,0},{0,1},{1,1}}, 4},
{'S', (Point[]){{1,0},{2,0},{0,1},{1,1}}, 4},
{'T', (Point[]){{1,0},{0,1},{1,1},{2,1}}, 4},
{'Z', (Point[]){{0,0},{1,0},{1,1},{2,1}}, 4},
{'U', (Point[]){{0,0},{2,0},{0,1},{1,1},{2,1}}, 5}, // 2x3 矩形 (0,0)-(2,1),挖去 (1,0)
{'V', (Point[]){{1,0},{0,1},{1,1}}, 3}, // 2x2 矩形 (0,0)-(1,1),挖去 (0,0)
{'X', (Point[]){{1,0},{0,1},{1,1},{2,1},{1,2}}, 5},//T形加上顶部1块
{0, NULL, 0}
};
// 2. 替换 search 函数为以下代码
// 交换整数
void swap_int(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
// Fisher-Yates 洗牌
void shuffle_ints(int *array, int n) {
for (int i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
swap_int(&array[i], &array[j]);
}
}
// ---------- 辅助函数 ----------
ShapeBase *find_shape_base(char letter) {
for (int i = 0; shape_bases[i].letter != 0; i++) {
if (shape_bases[i].letter == letter)
return &shape_bases[i];
}
return NULL;
}
// 旋转点集
void rotate_points(Point *in, int n, int rot, Point *out) {
for (int i = 0; i < n; i++) {
int x = in[i].x, y = in[i].y;
switch (rot) {
case 0: out[i].x = x; out[i].y = y; break; // 0°
case 1: out[i].x = -y; out[i].y = x; break; // 90°
case 2: out[i].x = -x; out[i].y = -y; break; // 180°
case 3: out[i].x = y; out[i].y = -x; break; // 270°
}
}
}
// 规范化点集(平移使 min_x = min_y = 0),并返回尺寸
void normalize_points(Point *pts, int n, int *min_x, int *min_y, int *max_x, int *max_y) {
int mx = pts[0].x, my = pts[0].y;
int Mx = pts[0].x, My = pts[0].y;
for (int i = 1; i < n; i++) {
if (pts[i].x < mx) mx = pts[i].x;
if (pts[i].y < my) my = pts[i].y;
if (pts[i].x > Mx) Mx = pts[i].x;
if (pts[i].y > My) My = pts[i].y;
}
for (int i = 0; i < n; i++) {
pts[i].x -= mx;
pts[i].y -= my;
}
*min_x = 0; *min_y = 0;
*max_x = Mx - mx;
*max_y = My - my;
}
// 点集排序(简单冒泡,点数极少)
void sort_points(Point *pts, int n) {
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (pts[j].x > pts[j+1].x || (pts[j].x == pts[j+1].x && pts[j].y > pts[j+1].y)) {
Point tmp = pts[j];
pts[j] = pts[j+1];
pts[j+1] = tmp;
}
}
}
}
// 比较两个点集是否相同(假设已排序)
bool points_equal(Point *a, Point *b, int n) {
for (int i = 0; i < n; i++)
if (a[i].x != b[i].x || a[i].y != b[i].y) return false;
return true;
}
// 生成形状的旋转信息
ShapeInfo generate_shape_info(ShapeBase *base) {
ShapeInfo info;
info.letter = base->letter;
info.num_rotations = 0;
info.rotations = malloc(4 * sizeof(Rotation)); // 最多4个旋转
for (int rot = 0; rot < 4; rot++) {
Point temp_pts[4]; // 最多4格
rotate_points(base->offsets, base->num_cells, rot, temp_pts);
// 规范化
int min_x, min_y, max_x, max_y;
normalize_points(temp_pts, base->num_cells, &min_x, &min_y, &max_x, &max_y);
// 排序
sort_points(temp_pts, base->num_cells);
// 检查是否重复
bool duplicate = false;
for (int i = 0; i < info.num_rotations; i++) {
if (info.rotations[i].num_points == base->num_cells &&
points_equal(info.rotations[i].points, temp_pts, base->num_cells)) {
duplicate = true;
break;
}
}
if (!duplicate) {
Point *pts = malloc(base->num_cells * sizeof(Point));
memcpy(pts, temp_pts, base->num_cells * sizeof(Point));
Rotation *r = &info.rotations[info.num_rotations];
r->points = pts;
r->num_points = base->num_cells;
r->width = max_x - min_x + 1; // min_x = 0
r->height = max_y - min_y + 1;
info.num_rotations++;
}
}
return info;
}
// 释放 ShapeInfo
void free_shape_info(ShapeInfo *info) {
for (int i = 0; i < info->num_rotations; i++)
free(info->rotations[i].points);
free(info->rotations);
}
// ---------- DLX 操作 ----------
void cover(Node *col) {
col->left->right = col->right;
col->right->left = col->left;
Node *i = col->down;
while (i != col) {
Node *j = i->right;
while (j != i) {
j->down->up = j->up;
j->up->down = j->down;
j->col->size--;
j = j->right;
}
i = i->down;
}
}
void uncover(Node *col) {
Node *i = col->up;
while (i != col) {
Node *j = i->left;
while (j != i) {
j->col->size++;
j->down->up = j;
j->up->down = j;
j = j->left;
}
i = i->up;
}
col->left->right = col;
col->right->left = col;
}
// 3. 修正后的 shuffle 函数:专门为 Node** 数组编写
void shuffle_nodes(Node **array, int n) {
for (int i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
swap_int((int*)&array[i], (int*)&array[j]); // 安全地交换指针值
}
}
// 4. 修正后的 search_random 函数
bool search_random(Node *header_node) {
if (header_node->right == header_node) return true;
// 选择最小 size 的列
Node *col = header_node->right;
Node *best = col;
int min_size = col->size;
col = col->right;
while (col != header_node) {
if (col->size < min_size) {
min_size = col->size;
best = col;
if (min_size == 0) break;
}
col = col->right;
}
if (min_size == 0) return false;
cover(best);
// 收集行
Node **rows_array = malloc(min_size * sizeof(Node*));
Node *r = best->down;
int count = 0;
while (r != best) {
rows_array[count++] = r;
r = r->down;
}
// 打乱行
shuffle_nodes(rows_array, min_size);
// 遍历尝试
for (int i = 0; i < min_size; i++) {
r = rows_array[i];
int shape_idx = row_to_shape[r->rowid];
if (shape_remaining[shape_idx] > 0) {
// 1. Cover all columns in this row
Node *j = r->right;
while (j != r) {
cover(j->col);
j = j->right;
}
shape_remaining[shape_idx]--;
solution_rows[depth++] = r->rowid;
if (search_random(header_node)) {
free(rows_array);
return true;
}
// 2. Backtrack: Uncover columns
depth--;
shape_remaining[shape_idx]++;
// 必须从右向左遍历 uncover,且需要再次遍历 r 的列
// 因为没有 left 指针,我们先收集这些列,或者再次遍历 r
// 这里再次遍历 r 收集列,然后逆序 uncover
Node **cols_to_uncover = malloc(min_size * sizeof(Node*));
int c_count = 0;
Node *temp = r->right;
while (temp != r) {
cols_to_uncover[c_count++] = temp->col;
temp = temp->right;
}
for (int k = c_count - 1; k >= 0; k--) {
uncover(cols_to_uncover[k]);
}
free(cols_to_uncover);
}
}
free(rows_array);
uncover(best);
return false;
}
bool search(Node *header_node) {return search_random(header_node);}
// ---------- 主函数 ----------
int main() {
char input[1000];
// 在 main 函数内部开头添加
srand(time(NULL));
if (fgets(input, sizeof(input), stdin) == NULL) {
fprintf(stderr, "No input\n");
return 1;
}
// 移除换行符
input[strcspn(input, "\n")] = 0;
// 移除所有空格(简化解析)
char *src = input, *dst = input;
while (*src) {
if (*src != ' ') *dst++ = *src;
src++;
}
*dst = 0;
// 解析宽度和高度
char *p = strchr(input, '[');
if (!p) { fprintf(stderr, "Invalid input format\n"); return 1; }
char head[100];
strncpy(head, input, p - input);
head[p - input] = '\0';
if (sscanf(head, "%d,%d", &W, &H) != 2) {
fprintf(stderr, "Parse error: width and height\n");
return 1;
}
// 解析形状列表
char *q = strrchr(input, ']');
if (!q) { fprintf(stderr, "Missing ']'\n"); return 1; }
char inside[500];
strncpy(inside, p + 1, q - p - 1);
inside[q - p - 1] = '\0';
input_shapes = malloc(10 * sizeof(ShapeItem)); // 假设最多10种形状
input_shape_count = 0;
char *token = strtok(inside, ",");
while (token) {
char letter = token[0];
token = strtok(NULL, ",");
if (!token) { fprintf(stderr, "Missing count for %c\n", letter); return 1; }
int count = atoi(token);
input_shapes[input_shape_count].letter = letter;
input_shapes[input_shape_count].count = count;
input_shape_count++;
token = strtok(NULL, ",");
}
// 为每个输入形状生成旋转信息
shapes_info = malloc(input_shape_count * sizeof(ShapeInfo));
for (int i = 0; i < input_shape_count; i++) {
char letter = input_shapes[i].letter;
ShapeBase *base = find_shape_base(letter);
if (!base) {
fprintf(stderr, "Unsupported shape letter: %c\n", letter);
return 1;
}
shapes_info[i] = generate_shape_info(base);
}
// 计算总放置数 (dry run)
total_rows = 0;
for (int i = 0; i < input_shape_count; i++) {
ShapeInfo *info = &shapes_info[i];
for (int r = 0; r < info->num_rotations; r++) {
Rotation *rot = &info->rotations[r];
int px = W - rot->width + 1;
int py = H - rot->height + 1;
if (px > 0 && py > 0)
total_rows += px * py;
}
}
if (total_rows == 0) {
printf("No solution\n");
return 0;
}
// 分配数组
row_to_shape = malloc(total_rows * sizeof(int));
row_first_nodes = malloc(total_rows * sizeof(Node*));
solution_rows = malloc(total_rows * sizeof(int));
// 构建列头 (保持不变)
total_cols = W * H;
col_headers = malloc(total_cols * sizeof(Node));
header = malloc(sizeof(Node));
// 初始化 header
header->right = header;
header->left = header;
header->up = header;
header->down = header;
header->rowid = -1;
header->size = 0;
// 初始化列头并连接
for (int i = 0; i < total_cols; i++) {
Node *c = &col_headers[i];
c->left = c;
c->right = c;
c->up = c;
c->down = c;
c->col = c;
c->rowid = -1;
c->size = 0;
// 插入 header 左侧链表
c->right = header;
c->left = header->left;
header->left->right = c;
header->left = c;
}
// --- 新增:用于暂存生成的行节点的数组 ---
Node **temp_row_buffer = malloc(total_rows * sizeof(Node*));
int temp_row_count = 0;
// 1. 生成阶段:创建节点,只连接行内左右,暂不插入列上下关系
int current_row_id = 0;
for (int idx = 0; idx < input_shape_count; idx++) {
ShapeInfo *info = &shapes_info[idx];
for (int r = 0; r < info->num_rotations; r++) {
Rotation *rot = &info->rotations[r];
int px = W - rot->width + 1;
int py = H - rot->height + 1;
if (px <= 0 || py <= 0) continue;
for (int i = 0; i < px; i++) {
for (int j = 0; j < py; j++) {
Node *row_start = NULL;
Node *prev = NULL;
for (int p = 0; p < rot->num_points; p++) {
int x = i + rot->points[p].x;
int y = j + rot->points[p].y;
int col_idx = y * W + x;
Node *node = malloc(sizeof(Node));
node->rowid = current_row_id;
node->col = &col_headers[col_idx];
// 只做左右连接 (行结构)
if (row_start == NULL) {
row_start = node;
node->left = node;
node->right = node;
prev = node;
} else {
node->right = row_start;
node->left = prev;
prev->right = node;
row_start->left = node;
prev = node;
}
}
// 存入缓冲区
temp_row_buffer[temp_row_count++] = row_start;
row_to_shape[current_row_id] = idx;
row_first_nodes[current_row_id] = row_start;
current_row_id++;
}
}
}
}
// 2. 打乱阶段:对缓冲区进行 Fisher-Yates 洗牌
// 交换函数
void swap_nodes(Node **a, Node **b) {
Node *t = *a; *a = *b; *b = t;
}
// 洗牌
for (int i = temp_row_count - 1; i > 0; i--) {
int j = rand() % (i + 1);
swap_nodes(&temp_row_buffer[i], &temp_row_buffer[j]);
}
// 3. 插入阶段:将打乱后的行插入到列的上下链表中
for (int k = 0; k < temp_row_count; k++) {
Node *r = temp_row_buffer[k];
Node *curr = r;
do {
// 插入到对应列的底部
Node *col = curr->col;
curr->up = col->up;
curr->down = col;
col->up->down = curr;
col->up = curr;
col->size++; // 更新列的大小
curr = curr->right;
} while (curr != r);
}
// 释放临时缓冲区
free(temp_row_buffer);
// 初始化形状剩余数量
shape_remaining = malloc(input_shape_count * sizeof(int));
for (int i = 0; i < input_shape_count; i++)
shape_remaining[i] = input_shapes[i].count;
depth = 0;
// 执行搜索 (使用原有的 search 函数)
bool found = search(header);
if (found) {
// 创建网格
char **grid = malloc(H * sizeof(char*));
for (int y = 0; y < H; y++) {
grid[y] = malloc(W + 1);
for (int x = 0; x < W; x++)
grid[y][x] = '?';
grid[y][W] = '\0';
}
// 填充网格
for (int i = 0; i < depth; i++) {
int rowid = solution_rows[i];
int shape_idx = row_to_shape[rowid];
char letter = shapes_info[shape_idx].letter;
Node *node = row_first_nodes[rowid];
Node *start = node;
Node *cur = start;
do {
int col_idx = cur->col - col_headers;
int x = col_idx % W;
int y = col_idx / W;
grid[y][x] = letter;
cur = cur->right;
} while (cur != start);
}
// 输出
for (int y = 0; y < H; y++) {
for (int x = 0; x < W; x++) {
printf("%c", grid[y][x]);
if (x < W - 1) printf(" ");
}
printf("\n");
free(grid[y]);
}
free(grid);
} else {
printf("No solution\n");
}
// 释放内存(略,程序结束可不释放)
// 清理形状信息
for (int i = 0; i < input_shape_count; i++)
free_shape_info(&shapes_info[i]);
free(shapes_info);
free(input_shapes);
free(shape_remaining);
free(row_to_shape);
free(row_first_nodes);
free(solution_rows);
free(col_headers);
free(header);
return 0;
}
把以上两版分别命名为dlxtrnd.c和dlxtrnd2.c,未打乱的版本dlxt2.c。
现在打乱版本能求解较大的矩形了。而未打乱的版本求不出来。
gcc dlxtrnd2.c -o dlxtrnd2 -O3
gcc dlxtrnd.c -o dlxtrnd -O3
gcc dlxt2.c -o dlxt2 -O3
time echo 20,18,[C,12,V,24,L,12,O,12,S,12,T,12,X,12]| ./dlxtrnd
S T T T V V V V V V T C C C C C C C C C
S S T V V V V V V V T T L L L T C C C L
...
C C L S S L O O V S S V X T X X X V V V
L L L L L L O O S S V V T T T X V V V V
real 0m38.189s
user 0m38.178s
sys 0m0.010s
time echo 20,18,[C,12,V,24,L,12,O,12,S,12,T,12,X,12]| ./dlxtrnd2
L O O C S T T T V T T T L L L V V V V T
L O O C S S T V V L T X L L L V C V T T
...
C V L V S S X V O O X S S T S L L O O L
L L L V V S V V O O S S T T T L L O O L
real 0m38.439s
user 0m38.407s
sys 0m0.002s
time echo 20,18,[C,12,V,24,L,12,O,12,S,12,T,12,X,12]| ./dlxt2
^C
real 1m39.898s
user 1m39.903s
sys 0m0.001s

838

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



