poj_1753_高斯消元

开心,总算过了,对于高斯消元的理解更深一步,这道题的重点在于,如果用高斯消元的话,最后一行是要枚举的,总共16种情况,因为不知道怎么枚举,当时卡了好久,其实在线性方程组中,我们可以当做缺少了四个方程,所以我们的过程就是枚举最后四个方程,然后一步一步往回带入。
其中要注意一点,就是最后的结果可以全是白面,也可以全是黑面,看了很多博客,说的解法都很麻烦,其实大可以不必,只要a数组加两列就行,将枚举的结果回带的时候,要多开两个数组记录下解空间,仅此而已。

/*#########################################################################
# File Name: poj_1753.cpp
# Author: CaoLei
# Created Time: 2015/7/21 14:02:56
#########################################################################*/

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
using namespace std;
#define MAX(x,y) (((x)>(y))?(x):(y))
#define MIN(x,y) (((x)<(y))?(x):(y))
#define N 500010
#define pi acos(-1.0)
#define inf 100000000
typedef long long ll;
typedef unsigned long long ull;
char str[10];
int a[20][20];
int x1[20];
int x2[20];
int gauss(){
    //高斯消元基本过程
    int i = 0, line = 0;
    for (;i<16;){
        int p = line;
        for (int j = line + 1; j<16; j++) if (a[line][i]<a[j][i]) p = j;
        if (p != line) for (int j = 0; j <= 17; j++) swap(a[line][j], a[p][j]);
        if (a[line][i] == 0){
            i++;
            continue;
        }
        for (int j = 0; j<16; j++){
            if (j != line&&a[j][i]){
                for (int k = i; k <= 17; k++) a[j][k] ^= a[line][k];
            }
        }
        i++;
        line++;
    }
    bool f1 = true, f2 = true;
    for (int k = line; k<16; k++){
        if (a[k][16]) f1 = false;
        if (a[k][17]) f2 = false;
    }
    if (!f1&&!f2) return -1;
    if(line==16) return 1;
    int ans=16;
    //这里记录下自由元的个数
    int t=16-line;
    //枚举16种解的情况
    for(int i=0;i<(1<<t);i++){
        for(int j=0;j<16;j++){
            x1[j]=a[j][16];
            x2[j]=a[j][17];
        }
        for(int j=0;j<t;j++){
            if(i&(1<<j)){
                x1[15-j]=1;
                x2[15-j]=1;
            }
            else {
                x1[15-j]=0;
                x2[15-j]=0;
            }
        }
        //回带的过程
        for(int j=16-t-1;j>=0;j--){
            int tmp1=0,tmp2=0;
            for(int k=j+1;k<16;k++){
                if(a[j][k]) {
                    tmp1^=x1[k];
                    tmp2^=x2[k];
                }
            }
            //维护解空间
            x1[j]^=tmp1;
            x2[j]^=tmp2;
        }
        int c1=0,c2=0;
        for(int ii=0;ii<16;ii++){
            if(x1[ii]) c1++;
            if(x2[ii]) c2++;
        }
        //更新最小值
        ans=MIN(ans,MIN(c1,c2));
    }
    return ans;
}

int main(){
    //freopen("in.txt","r",stdin);
    int nn=20;
        memset(a,0,sizeof(a));
        for (int i = 0; i<4; i++){
            scanf("%s", str);
            for (int j = 0; j<4; j++){
                int tmp = i * 4 + j;
                if (str[j] == 'b') {
                    a[tmp][16] = 0;
                    a[tmp][17] = 1;
                }
                else{
                    a[tmp][16] = 1;
                    a[tmp][17] = 0;
                }
                a[tmp][tmp] = 1;
                if (tmp>3) a[tmp - 4][tmp] = 1;
                if (tmp<12) a[tmp + 4][tmp] = 1;
                if (tmp % 4 != 0) a[tmp - 1][tmp] = 1;
                if (tmp % 4 != 3) a[tmp + 1][tmp] = 1;
            }
        }
        int ans = gauss();
        if (ans == -1) printf("Impossible\n");
        else printf("%d\n", ans);
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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、付费专栏及课程。

余额充值