POJ 3279 Fliptile (二进制枚举)

 <题目链接>

<转载于 >>> >

题目大意:
 给定一个M*N矩阵,有些是黑色(1表示)否则白色(0表示),每翻转一个(i,j),会使得它和它周围4个格变为另一个颜色,要求翻转最少的点,使得变为全白色的矩阵,输出这个标记了翻转点的矩阵,如果有多个最优解,输出字典序最小的那个矩阵,若没有解,输出IMPOSSIBLE。

解题分析:

由于一个点翻转两次则返回原来的状态,所以最优解每个点最多翻转一次,但是2^(M*N)过大,所以2^N枚举第一行的所有翻转方式(逆字典序枚举),确定一种方式之后第二行也就随之确定了,因为如果第一行处理后没有翻回白色的点:(i,j),必须在第二行(i+1,j)翻回,否则将无法返回。反之第二行其他的点都处理为不翻转,要不然上一行的点会翻回黑色而无法改变。第二行ok后同理解决第三行,以此类推。处理到最后一行如果不是全白就输出IMPOSSIBLE。否则更新结果。

即,用二进制枚举第一行的翻转情况,然后2~n-1行按照上一行的情况来翻转,最后再判断最后一行是否全部为0,如果为0,则记录下翻转次数,随时更新答案。

 
#include<cstdio>
#include<cstring>
int t[30][30], tem[30][30], m[30][30];
//这里用一个数组记录翻转次数,再配合原来的点数,就能判断反转后的点数,这里很巧妙
int M,N,dir[5][2] = { 0,0,1,0,0,1,-1,0,0,-1 };

int get(int x, int y)//获得x,y点的颜色       //它本身的点数,再加上周围四个点反转的次数,就能得到它的真实点数
{
    int c = t[x][y];
    for (int i = 0; i < 5; i++)
    {
        int x1 = x + dir[i][0], y1 = y + dir[i][1];
        c += tem[x1][y1];
    }
    return c % 2;
}

int cal()  //计算2行及之后的,有解返回翻点数,无解返回-1
{
    for (int i = 2; i <= M; i++)
        for (int j = 1; j <= N; j++)
            if (get(i - 1, j) == 1)
                tem[i][j] = 1;
    //得到前n-1行的翻转次数

    for (int i = 1; i <= N; i++)
        if (get(M, i))return -1;      //如果最后一行有一个点不为0,说明枚举的第一行不符合要求s
    int res = 0;
    for (int i = 1; i <= M; i++)
        for (int j = 1; j <= N; j++)
            res += tem[i][j];
    return res;    //记录下需要翻转的总次数
}

int main()
{
    int min = -1;   //次数>0可以这样初始化
    scanf("%d%d", &M, &N);
    for (int i = 1; i <= M; i++)
        for (int j = 1; j <= N; j++)
            scanf("%d", &t[i][j]);
    for (int i = 0; i < (1 << N); i++)    //枚举第一行的所有情况
    {
        memset(tem, 0, sizeof(tem));    //初始化翻转数组
        for (int j = 1; j <= N; j++)
            tem[1][j] = (i >> (j - 1)) & 1;     //根据二进制得到第一行的翻转情况,这个技巧一定要掌握

        int num = cal();
        if (num >= 0 && (min<0 || min>num))    //取情况成立并且总翻转次数最小的
        {
            min = num;
            memcpy(m, tem, sizeof(tem));      //记录下最后的翻转矩阵
        }
    }

    if (min == -1)printf("IMPOSSIBLE
");
    else
    {
        for (int i = 1; i <= M; i++)
            for (int j = 1; j <= N; j++)
                printf("%d%c", m[i][j], j == N ? '
' : ' ');
    }
    return 0;
}

2018-08-30

原文地址:https://www.cnblogs.com/00isok/p/9560386.html