Fliptile POJ - 3279 (局部枚举 + 状态压缩)

https://vjudge.net/problem/POJ-3279

参考博客: https://blog.csdn.net/loy_184548/article/details/50949972

这道题目在vj上的kuangbin比赛中被归类到了搜索, 其实它还算不上搜素, 是一道比较基础的枚举题目,和熄灯问题, 画家问题,拨钟问题是一类题, 如果单纯用爆搜或者枚举来考虑的话可能会超时,复杂度O(2^N*M) 需要一点点小的技巧

首先, 我们来考虑一下, 改变当前一行的只有按下当前一行或者是上一行正对着的按钮.

所以, 我们只用考虑第一行, 枚举第一行的开关操作, 然后与之对应的第二行就确定了, 第二行又确定了第三行...以此类推, 直到最后一行. 我们来判断是否全部归0

需要注意的是题中要求输出最优解, 多个最优解又要求以字典序最小的输出, 我在此用了一个字典序的状态压缩, 可以直接拿走下次用.

//Flip Tile 枚举
#include <stdio.h>
#include<cstring>
const int maxn = 20;
int M, N;
int color[maxn][maxn], cur[maxn][maxn];//题中读入颜色, 当前操作颜色(防止直接改变源颜色)
int oper[maxn][maxn], ans[maxn][maxn], steps = 0, minSteps = 1<<30; //当前操作, 最小操作, 当前解和最小解
void press(int x, int y)
{ //按下x, y处的按钮
    cur[x][y]^=1, cur[x+1][y]^=1, cur[x-1][y]^=1, cur[x][y+1]^=1, cur[x][y-1]^=1;
}
bool solve()
{ //判断是否已经解决问题
    memcpy(cur, color, sizeof(color));
    //根据枚举结果改变第一二行
    for(int i = 1; i <= N; i++)
        if(oper[1][i])
            press(1, i), steps++;
    //根据第i-1行决定第i行的操作
    for(int i = 2; i <= M; i++){
        for(int j = 1; j <= N; j++)
            if(cur[i-1][j])
                oper[i][j]=1, press(i, j), steps++;
    }
     //判断最后一行是否满足条件
    for(int i = 1; i <= N; i++)
        if(cur[M][i]) return 0;
    return 1;
}
int main()
{
    scanf("%d%d",&M,&N);
    for(int i = 1; i <= M; i++)
        for(int j = 1; j <= N; j++)
            scanf("%d",&color[i][j]);
    //仅仅枚举第一行的状态即可
    for(int i = 0; i < (1<<N); i++){ //状态压缩
        memset(oper, 0, sizeof(oper)), steps = 0; //初始化不要忘
        for(int j = 0; j < N; j++){
            oper[1][N-j] = (i>>j&1);
        }
        if(solve() && steps>0 && steps<minSteps)
            minSteps = steps, memcpy(ans, oper, sizeof(oper));;

    }
    if(minSteps < (1<<30))
        for(int i = 1; i <= M; i++){
            for(int j = 1; j <= N; j++)
                printf("%d ",ans[i][j]);
            printf("\n");
        }
    else printf("IMPOSSIBLE\n");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a1097304791/article/details/82997059