poj 1222 (枚举or高斯消元)

传送门:点击打开链接

题意:给你一个5*6的矩阵,每个点上都有一个灯,按下f[i][j]的按钮,f[i][j]位置的灯的状态会改变,它上下左右的灯的状态也会改变(开变关,关变开)。现在给出这个矩阵的初始状态,输出按下哪些按钮,使所有的灯都关闭。

分析:

解法一(穷举法):我们可以先二进制枚举第一行所有的翻转情况,才64种。之后第一行已经确定了,然后看第2行,如果第一行某个位置为1,那么在同一列的第2行这个位置必须反转。。(因为第一行已经确定,不能再反转了,而影响第一行的,也只有在同一列的第2行),这样我们可以确定了,前N-1行,全部为0,最后单独检查最后一行,是否全部为0。

解法二(高斯消元):每个位置可以列出一个方程, 列出增广矩阵: 每个位置可以形成增广矩阵的一行, 每行都有30个系数 分别代表(0到29号灯), 将 可以影响该位置改变的 位置(自己、上、下、左、右)对应的置1, 其余置0,这样就形成了30*30的系数矩阵。将初始状态置入最后一列,就形成了增广矩阵。这里是解亦或方程组,注意与解普通方程组的区别,把加减改成亦或,乘法改成&&,代码参考大牛写的。。。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
int n,mp[10][10],tp[10][10],b[10][10];

int gb(int x,int y) {
    int s=b[x][y];
    if(x>0) s+=b[x-1][y];
    if(y>0) s+=b[x][y-1];
    if(y<5) s+=b[x][y+1];
    return s;
}

void sv() {
    for(int i=0;i<(1<<6);i++) {
        memset(tp,0,sizeof(tp));
        for(int j=0;j<6;j++) {
            if( i&(1<<(5-j)) )  b[0][j]=1;
            else b[0][j]=0;
        }
        for(int j=0;j<6;j++) {
            int s=b[0][j];
            if(j>0) s+=b[0][j-1];
            if(j<5) s+=b[0][j+1];
            if(s%2) tp[0][j]=1^mp[0][j];
            else tp[0][j]=mp[0][j];
        }
        for(int j=1;j<5;j++) {
            for(int k=0;k<6;k++) {
                if(tp[j-1][k]) b[j][k]=1;
                else b[j][k]=0;
            }
            for(int k=0;k<6;k++) {
                int sum=gb(j,k);
                if(sum%2) tp[j][k]=1^mp[j][k];
                else tp[j][k]=mp[j][k];
            }
        }
        int s=0;
        for(int j=0;j<6;j++)
            s+=tp[4][j];
        if( s==0 ) {
            for(int j=0;j<5;j++){
                for(int k=0;k<6;k++) {
                    cout<<b[j][k];
                    if( k!=5 ) cout<<" ";
                }
                cout<<endl;
            }
        }
    }
}

int main() {
    cin>>n;
    for(int t=1;t<=n;t++) {
        for(int i=0;i<5;i++)
            for(int j=0;j<6;j++)
                cin>>mp[i][j];
        cout<<"PUZZLE #"<<t<<endl;
        sv();
    }
    return 0;
}


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int a[300][300];  /// 增广矩阵
int x[300];  /// 解
int free_x[300]; /// 标记是否为自由未知量

int n, m;

void Gauss(int n, int m) /// n个方程 m个未知数 即 n行m+1列
{
    ///转换为阶梯形式
    int col=0, k, num=0;
    for(k=0;k<n && col<m;k++, col++) {///枚举行
        int max_r=k;
        for(int i=k+1;i<n;i++)///找到第col列元素绝对值最大的那行与第k行交换
            if(abs(a[i][col])>abs(a[max_r][col]))
                max_r=i;
        for(int j=col;j<m+1;j++)/// 与第k行交换
            swap(a[k][j], a[max_r][j]);
        /*
        if(!a[k][col])// 说明该col列第k行以下全是0了 
        {
            k--;
            free_x[num++]=col;
            continue;
        }
        */
        for(int i=k+1;i<n;i++)/// 枚举要删除的行
            if(a[i][col])
                for(int j=col;j<m+1;j++)
                    a[i][j]^=a[k][j];
    }
    ///  唯一解 回代
    for(int i=m-1;i>=0;i--){
        x[i]=a[i][m];
        for(int j=i+1;j<m;j++)
            x[i]^=(a[i][j] && x[j]);
    }
}


void init(){
    n=5, m=6;
    memset(a, 0, sizeof(a));
    memset(x, 0, sizeof(x));
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            int t=i*m+j;
            a[t][t]=1;
            if(i>0) a[(i-1)*m+j][t]=1;
            if(i<n-1) a[(i+1)*m+j][t]=1;
            if(j>0) a[i*m+j-1][t]=1;
            if(j<m-1) a[i*m+j+1][t]=1;
        }
}

int main(){
    int t, ca=1;
    scanf("%d", &t);
    while(t--){
        init();
        for(int i=0;i<n*m;i++)
            scanf("%d", &a[i][n*m]);
        printf("PUZZLE #%d\n", ca++);
        Gauss(n*m, n*m);
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++){
                printf("%d", x[i*m+j]);
                if(j==5) printf("\n");
                else printf(" ");
            }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tianwei0822/article/details/80366054