POJ 1753 Flip Game 解题报告(高斯消元法)

96 篇文章 0 订阅
6 篇文章 0 订阅

    题目大意:翻动一枚棋子,周围四枚棋子也要翻动,问最少翻动几次可以让棋盘上所有棋子都是黑色或者都是白色。

    解题报告:这道题做法很多,可以用BFS,暴搜+位运算。这些写起来都不难,而且都可以A掉。不过最近在学习高斯消元法,所以仍然用高斯消元法去做。如果题目变成8*8,高斯消元依然可以做,暴搜就不一定了= =。

    这题用高斯消元的难度在于矩阵是存在4个变元的,而且直接求解的话不能求得任何一个变元。在下也不会,不过搜一搜就会了。

    在利用初等行变换来把增广矩阵转换成行阶梯阵时,如果col列中的元素全为0,那么此时的col列就是变元。此时记录col列的位置。

    我们可以在化简完矩阵后对变元进行枚举。该题中变元的值不是0就是1,可以直接使用位运算确定每个变元的值。然后根据已经枚举的值去确定剩下的非变元,并寻找优化的解。注意全1或者全0都是解,所以求解两次。

    代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

typedef int Matrix[16][17];

int Gauss(Matrix a, int equ, int var)
{
    bool free[17];
    memset(free, 0, sizeof(free));
    int freePos[17];
    int freeNum=0;
    int x[17];

    int row=0;
    int col=0;
    while(row<equ && col<var)
    {
        int r=row;
        for(int i=row+1;i<equ;i++) if(a[i][col])
            r=i;
        if(a[r][col]==0)
        {
            free[col]=true;
            freePos[freeNum++]=col;
            col++;
            continue;
        }

        for(int j=col;j<=var;j++)
            swap(a[row][j], a[r][j]);

        for(int i=row+1;i<equ;i++) if(a[i][col])
            for(int j=col;j<=var;j++)
                a[i][j]^=a[row][j];
        row++;
        col++;
    }

    for(int i=row;i<equ;i++) if(a[i][col])
        return 20;

    int res=20;

    for(int s=0;s<(1<<freeNum);s++)
    {
        int cnt=0;
        int binary=s;
        memset(x, 0, sizeof(x));

        for(int j=0;j<freeNum;j++, binary>>=1) if(binary&1)
        {
            x[freePos[j]]=1;
            cnt++;
        }

        for(int i=row-1;i>=0;i--) if(!free[i])
        {
            int tmp=a[i][var];
            for(int j=i+1;j<var;j++) if(a[i][j])
                tmp^=x[j];
            x[i]=tmp;
            if(tmp) cnt++;
        }
        res=min(res, cnt);
    }
    return res;
}

int main()
{
    int b[16];
    for(int i=0;i<16;)
    {
        char ch=getchar();
        if(ch=='\n')
            continue;
        b[i]=(ch=='b'?1:0);
        i++;
    }

    Matrix a;
    memset(a, 0, sizeof(a));
    for(int i=0;i<16;i++)
    {
        a[i][16]=b[i];
        a[i][i]=1;
        if(i<12)
            a[i][i+4]=1;
        if(i>=4)
            a[i][i-4]=1;
        if(i%4!=0)
            a[i][i-1]=1;
        if(i%4!=3)
            a[i][i+1]=1;
    }
    int Min=Gauss(a, 16, 16);

    memset(a,0,sizeof(a));
    for(int i=0;i<16;i++)
    {
        a[i][16]=1-b[i];
        a[i][i]=1;
        if(i<12)
            a[i][i+4]=1;
        if(i>=4)
            a[i][i-4]=1;
        if(i%4!=0)
            a[i][i-1]=1;
        if(i%4!=3)
            a[i][i+1]=1;
    }
    Min=min(Min,Gauss(a, 16, 16));

    if(Min==20)
        puts("Impossible");
    else
        printf("%d\n",Min);
}


    附BFS代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

typedef unsigned short Status;

bool vis[1<<16];
Status step[1<<16];
Status stack[1<<16];

Status change[16]=
{
    0x13,
    0x27,
    0x4E,
    0x8C,

    0x131,
    0x272,
    0x4E4,
    0x8C8,

    0x1310,
    0x2720,
    0x4E40,
    0x8C80,

    0x3100,
    0x7200,
    0xE400,
    0xC800
};

int bfs(Status n)
{
    memset(vis, 0, sizeof(vis));
    memset(step, 0, sizeof(step));

    int top=0, bot=0;
    stack[top++]=n;
    step[n]=0;
    vis[n]=true;

    while(bot<top)
    {
        Status now=stack[bot++];

        for(int i=0;i<16;i++) if(!vis[change[i]^now])
        {
            stack[top++]=change[i]^now;
            step[change[i]^now]=step[now]+1;
            vis[change[i]^now]=true;
        }

        if(vis[0]) return step[0];
        if(vis[(1<<16)-1]) return step[(1<<16)-1];
    }

    return -1;
}

int main()
{
    char ch[4][5];
    while(~scanf("%s%s%s%s", ch[0], ch[1], ch[2], ch[3]))
    {
        Status n=0;
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++) if(ch[i][j]=='b')
                n|=1<<(i*4+j);

        int ans = bfs(n);
        if(~ans) printf("%d\n", ans);
        else puts("Impossible");
    }
}



POJ1753题目为"Flip Game",题目给出了一个4x4的棋盘,每个格子有黑色或白色,每次翻转一个格子会同时翻转它上下左右四个格子的颜色,目标是把整个棋盘都变为同一种颜色,求把棋盘变成同种颜色的最小步数。 解题思路: 一般关于棋盘变色的题目,可以考虑使用搜索来解决。对于POJ1753题目,可以使用广度优先搜索(BFS)来解决。 首先,对于每个格子,定义一个状态,0表示当前格子是白色,1表示当前格子是黑色。 然后,我们可以把棋盘抽象成一个长度为16的二进制数,将所有格子的状态按照从左往右,从上往下的顺序排列,就可以用一个16位的二进制数表示整个棋盘的状态。例如,一个棋盘状态为: 0101 1010 0101 1010 则按照从左往右,从上往下的顺序把所有格子的状态连接起来,即可得到该棋盘的状态为"0101101001011010"。 接着,我们可以使用队列来实现广度优先搜索。首先将初始状态加入队列中,然后对于队列中的每一个状态,我们都尝试将棋盘上的每个格子翻转一次,生成一个新状态,将新状态加入队列中。对于每一个新状态,我们也需要记录它是从哪个状态翻转得到的,以便在得到最终状态时能够输出路径。 在搜索过程中,我们需要维护每个状态离初始状态的步数,即将该状态转换为最终状态需要的最小步数。如果我们找到了最终状态,就可以输出答案,即最小步数。 代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值