洛谷 - P3164 - 和谐矩阵 - 高斯约旦消元法

为什么可以跑n立方,我也不知道,反正就是可以。
模2意义的,据说每一行可以存一个bitset,会比用bool更快(快32倍?)。

本题告诉我们一个道理:
高斯消元之后,每个变量的含义不变(虽然交换了两行,但是实际上那个位置的向量还是表示那个单元),不需要复原。
每个变量要前往的目标状态不一样。注意非自由变量要用新的右边来确定值。
并不是所有的自由变量都在右下角,有可能有完全空的列。
也可以在给每行赋值秩的同时指定该列是秩为第几的列,0表示空列。
可以在消元的同时对自由变量进行赋值,非自由变量立刻由他决定。但是感觉很扯。

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int n,m,r;

inline int antii(int id) {
    int res=((id+m-1)/m);
    return res;
}

inline int antij(int id) {
    int res=id%m;
    if(res==0)
        res=m;
    return res;
}

inline int id(int,int);

bool tx[1800]= {};
bool allzero[1800]= {};
bool outtm[50][50]= {};

bool out() {
    int have1=0;
    for(int id=1; id<=n*m; id++) {
        outtm[antii(id)][antij(id)]=tx[id];
    }

    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            if(outtm[i][j]^outtm[i-1][j]^outtm[i+1][j]^outtm[i][j-1]^outtm[i][j+1]) {
                return false;
            }
            if(outtm[i][j])
                have1=1;
        }
    }

    if(!have1)
        return false;

    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++)
            printf("%d%c",(int)outtm[i][j]," \n"[j==m]);
    }

    return true;

    //高斯消元后,每个变量的目标已经改变了,不需要复原!!!
}


namespace Gauss_Jordan_Elimination {
    const int MAXN=1800;
    const int MAXM=1800;
    bool a[MAXN][MAXM+1];
    bool ans[MAXN];


    //返回增广矩阵的秩,-1表示无解
    int gauss_jordan(int n,int m) {

        int r=0;
        for(int i=1; i<=m; i++) {
            //当前是第i列
            int mx=-1;
            //从当前秩的下一行开始往下数
            for(int j=r+1; j<=n; j++)
                if(a[j][i]) {
                    mx=j;
                    break;
                }
            if(mx==-1) {
                //该列全0,被跳过
                continue;
            }
            r++;
            //增加一个线性基,当前秩增加
            if(mx!=r) {
                //需要交换
                for(int j=1; j<=m+1; j++)
                    //m+1表示增广矩阵
                    swap(a[r][j],a[mx][j]);
                //交换行
            }

            for(int j=1; j<=n; j++) {
                //枚举每一行
                if(j!=r&&a[j][i]) {
                    //消去除了r以外的其他行
                    //该行系数是当前列的tmp倍
                    for(int k=i; k<=m+1; k++) {
                        //把当前列对应行位置扩大tmp倍,然后用每个元素减去,由高斯约当的过程,左边的绝对是0
                        a[j][k]^=a[r][k];
                    }
                }
            }
        }

        return r;
    }
}

using namespace Gauss_Jordan_Elimination;


inline int id(int i,int j) {
    if(i>=1&&i<=n&&j>=1&&j<=m)
        return (i-1)*m+j;
    else
        return 0;
}

int dx[4]= {-1,1,0,0};
int dy[4]= {0,0,-1,1};

void dfs(int x,bool have1) {
    if(x==0) {
        if(have1) {
            if(out()) {
                exit(0);
            }
        }
        return;
    }
    if(allzero[x]) {
        tx[x]=1;
        dfs(x-1,1);
        tx[x]=0;
        dfs(x-1,have1);
    } else {
        bool v=a[x][n*m+1];
        for(int i=x+1; i<=n*m; i++) {
            v^=a[x][i]&tx[i];
        }
        tx[x]=v;
        dfs(x-1,have1|v);
    }
}

int main() {
    scanf("%d%d",&n,&m);

    {
        {
            memset(a,0,sizeof(a));
            for(int i=1; i<=n; i++) {
                for(int j=1; j<=m; j++) {
                    int tid=id(i,j);
                    a[tid][tid]=1;
                    for(int k=0; k<4; k++) {
                        int ttid=id(i+dx[k],j+dy[k]);
                        if(ttid) {
                            a[tid][ttid]=1;
                        }
                    }
                }
            }


            r=gauss_jordan(n*m,n*m);

            for(int i=1; i<=n*m; i++) {
                allzero[i]=1;
                for(int j=1; j<=n*m; j++) {
                    if(a[i][j]) {
                        allzero[i]=0;
                        break;
                    }
                }
            }

            dfs(n*m,0);
        }
    }

}

posted @ 2019-04-26 11:26  韵意  阅读(168)  评论(0编辑  收藏  举报