解决Flood-It-like拼图的最小点击次数

Fel*_*ipe 10 c++ algorithm backtracking

我有网格N×M,其中每个单元格用一种颜色着色.

当玩家点击颜色α网格的任何单元格时,网格最左上角的颜色为β的单元格接收颜色α,但不仅仅是:所有那些通过颜色连接到源的单元格仅使用颜色α或β的路径也接收颜色α.

应仅在水平和垂直方向上考虑单元之间的连接以形成路径.例如,当玩家点击左图中突出显示的单元格时,网格会在右侧接收图形的颜色.游戏的目标是使网格单色化.

点击结果

输入描述

输入的第一行由2个整数N和M(1≤N≤4,1≤M≤5)组成,它们分别代表网格的行数和列数.下面的N行描述了网格的初始配置,用0到9之间的整数表示每种颜色.输入不包含任何其他行.

输出描述

打印包含单个整数的行,该整数表示播放器必须执行的最小点击次数才能使网格变为单色.

输入样本

1:

4 5
01234
34567
67890
90123

2:

4 5
01234
12345
23456
34567

3:

4 5
00162
30295
45033
01837

输出样本

1:

12

2:

7

3:

10

我正在尝试找回带回溯的解决方案(因为8秒的时间限制和网格的小尺寸).但它超出了时间限制.有些人刚刚在0秒时完成了它.

还有其他算法可以解决这个问题吗?

#include <stdio.h>
#include <string.h>

#define MAX 5
#define INF 999999999

typedef int signed_integer;

signed_integer n,m,mink;
bool vst[MAX][MAX];

signed_integer flood_path[4][2] = {
    {-1,0},
    {1,0},
    {0,1},
    {0,-1}
};

//flood and paint all possible cells... the root is (i,j)
signed_integer flood_and_paint(signed_integer cur_grid[MAX][MAX],signed_integer i, signed_integer j, signed_integer beta, signed_integer alpha, signed_integer colors[]){
    //invalid cell
    if (vst[i][j] || i < 0 || i >= n || j < 0 || j >= m)
        return 0;

    //mark existent colors
    colors[cur_grid[i][j]] = 1;

    //only alpha and beta colors counts
    if (cur_grid[i][j] != beta && cur_grid[i][j] != alpha)
        return 0;

    //mark (i,j) as visited and change its color
    vst[i][j] = true;
    cur_grid[i][j] = alpha;

    //floodit !
    signed_integer ret = 1;
    for (signed_integer k = 0; k < 4; k++)
        ret += flood_and_paint(cur_grid,i + flood_path[k][0], j + flood_path[k][1], beta, alpha, colors);

    //how many cells change
    return ret;
}

void backtrack(signed_integer cur_grid[MAX][MAX],signed_integer k,signed_integer _cont, signed_integer alpha) {
    //bigger number of clicks for this solution ? ... getting back
    if(k >= mink)
        return;

    signed_integer colors[10];
    memset(vst, false, sizeof(vst));
    memset(colors, 0, sizeof(colors));

    signed_integer beta = cur_grid[0][0];
    signed_integer cont = flood_and_paint(cur_grid, 0, 0, beta, alpha, colors);

    //there are alpha colors to change and no beta colors to change
    colors[alpha] = 1;
    colors[beta]  = 0;

    //all squares on same color
    if (cont == n * m) {
        mink = k;
        return;
    }

    //this solution is equals to another ? ... getting back
    if (cont == _cont)
        return;

    ++k;//new click

    //copy this matrix and backtrack
    signed_integer copy[MAX][MAX];
    for (signed_integer c = 0; c < 10; ++c){
        if (colors[c] && c != cur_grid[0][0]) {
            memcpy(copy, cur_grid,n*m*sizeof(signed_integer));
            backtrack(copy,k,cont,c);
        }
    }
}

void cleanBuffer(){
     while (getchar() != '\n');
}

int main(void) {
    signed_integer grid[MAX][MAX];
    scanf("%d %d",&n,&m);
    for (signed_integer i = 0; i < n; ++i) {
        cleanBuffer();
        for (signed_integer j = 0; j < m; ++j){
            grid[i][j] = getchar() - '0';
        }
    }
    mink = INF;
    backtrack(grid,0, 0, grid[0][0]);
    printf("%d\n",mink);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*vaz 5

高水平的改进

请注意,单元格是其原始颜色或最后选择的颜色.

这意味着我们可以通过20位表示电路板的当前状态(标记每个4*5单元是否包含原始颜色),以及0到9范围内的数字,选择最后一种颜色.

这导致最多可探索1000万个州.回溯函数可以避免在它到达已经访问过的状态时必须递归.我希望这种改变可以使你的解决方案更快.

低水平的改善

通过20位掩码和最后一种颜色表示状态也使状态更新和恢复更快,因为只需要更改2个数字而不是整个板的memcpy.