利用小米mimo编写用DLX求解方块组成的不同形状(类似俄罗斯方块)精确覆盖矩形问题C程序

AI赋能编程语言挑战赛 10w+人浏览 297人参与

2025年12月17日,小米mimoAI工具发布了,赶紧试用一下。

编写用DLX求解方块组成的不同形状(类似俄罗斯方块)精确覆盖矩形问题C程序,参数,矩形宽度、高度,[形状,个数]列表。输出代表形状的字母填充的矩阵。比如A是1x1方块,B是1x2方块,求解"4,4,[A,4,B,6]",返回:

B B B B
B B B B
B B B A
A A B A

他深度思考了259秒,给出了如下程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.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 形
    {0, NULL, 0}
};

// ---------- 辅助函数 ----------
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;
}

// 搜索函数
bool search(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 *r = best->down;
    while (r != best) {
        int shape_idx = row_to_shape[r->rowid];
        if (shape_remaining[shape_idx] > 0) {
            // 覆盖该行其他列
            Node *j = r->right;
            while (j != r) {
                cover(j->col);
                j = j->right;
            }
            // 更新状态
            shape_remaining[shape_idx]--;
            solution_rows[depth++] = r->rowid;

            if (search(header_node))
                return true;

            // 回溯
            depth--;
            shape_remaining[shape_idx]++;
            // 解覆盖其他列
            j = r->left;
            while (j != r) {
                uncover(j->col);
                j = j->left;
            }
        }
        r = r->down;
    }
    uncover(best);
    return false;
}

// ---------- 主函数 ----------
int main() {
    char input[1000];
    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;
}

经测试,上述程序一次性编译通过,而且正确求出了一个简单的覆盖,

gcc dlxt.c -o dlxt

echo 4,4,[A,4,B,6]|./dlxt
A A A A
B B B B
B B B B
B B B B

源代码中给出了我提到的形状,以及他推测的形状,看了他的思考过程,发现他其实知道俄罗斯方块有7种标准形状,只是我的需求没有明确要求支持。关闭深度思考,继续提示

只给出俄罗斯方块7种标准形状:I, J, L, O, S, T, Z适用于上述程序的点集数组定义,不做别的

他给出了

// 替换 shape_bases 数组定义
ShapeBase shape_bases[] = {
    {'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},
    {0, NULL, 0}
};

我将上述代码合并到原有代码,就能解出标准俄罗斯方块的覆盖了。

echo 4,4,[O,1,L,2,I,1]|./dlxt
O O L L
O O L L
L L L L
I I I I

首次试用小米mimo,感觉还可以,和DeepSeek的水平相当,要是提示更精确一点,就不用思考那么长时间,同样能输出需要的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值