高斯消元在开关灯泡问题中的应用

  设其次方程组:

    a11*x1+a12*x2+a13*x3...+a1n*xn=b1,

    a21*x1+a22*x2+a23*x3...+a2n*xn=b2,

    .....

    am1*x1+am2*x2+am3*x3...+amn*xn=b3.


  写成矩阵形式:

  系数矩阵:

  a11   a12   ...   a1n

  a21   a22   ...   a2n

    .         .               .

   am1  am2 ...   amn


  解的矩阵

  b1

  b2

   .

  bn

 

  增广矩阵

  a11   a12   ...   a1n b1

  a21   a22   ...   a2n   b2

    .         .               .        .

   am1  am2 ...   amn  bn


  如果系数矩阵和增广矩阵的秩相等,有解(秩等于变元个数有唯一解,否则有多解)。秩不相等无解。


 对于灯泡开关问题,系数矩阵相当于操作矩阵,N个灯泡就构成N*(N+1)的矩阵,矩阵只由0,1组成,矩阵的关系不是相加而是异或!把一开始的状态赋给b1,b2...bn(a[i][N],下标从0开始),第一列代表第一个灯泡,把它和它相关的灯泡都设成1。比如第一个灯泡和第二、三个灯泡相关,就把a[1][1],a[2][1],a[3][1]设成1。把每个灯泡都这么做。构造完矩阵后求解就行了。

    这样构造矩阵的原因是,可以由灯泡的开关次数xi列出线性方程组,矩阵a元素值最大是1,因为重复开关没意义,xi的取值会影响和灯泡i相关的灯泡的取值。

   

    a11*x1^a12*x2^a13*x3...^a1n*xn=b1,

    a21*x1^a22*x2^a23*x3...^a2n*xn=b2,

    .....

    am1*x1^am2*x2^am3*x3...^amn*xn=b3.


   求解的时候可能0解,1解,多解,多解的时候可能会要求和最小的那个。有了模版每次只要构造矩阵就行了~ 得意

int equ,var;//equ个方程,var个变元
int a[MAXN][MAXN];//增广矩阵,行数为equ,列数为var+1,从0开始
int x[MAXN];//解集
int free_x[MAXN];//储存自由变元
int free_num;//自由变元个数
//针对灯泡问题的异或高斯消元
int xor_gauss(){
    int row=0,col=0;//当前矩阵阶梯下降了row次,当前在处理第col列
    free_num=0;
    for(;row<equ&&col<var;col++){
        int r;
        for(r=row;r<equ;r++) if(a[r][col]) break;//找当前row或者往下第一个a[r][col]!=0,r>=row
        //没找到,说明这一列矩阵阶梯没有下降,x[col]是自由变元,可以取任意值
        if(!a[r][col]){
            free_x[free_num++]=col;
            continue;
        }
        if(row!=r) for(int i=0;i<var+1;i++) swap(a[row][i],a[r][i]);//找到了,并且row!=r,把第row行和第r行交换
        //把row下面的行a[i][j]!=0的变为0,即和第row行相减
        for(int i=row+1;i<equ;i++) if(a[i][col]){
            for(int j=col;j<var+1;j++) a[i][j]^=a[row][j];
        }
        row++;//阶梯下降
    }
    for(int i=row;i<equ;i++) if(a[i][var]) return -1;//增广矩阵的秩如果大于方程组举证的秩,无解
    if(row<var) return var-row;//自由变元个数
    //唯一解,回带
    for(int i=var-1;i>=0;i--){
        x[i]=a[i][var];
        for(int j=i+1;j<var;j++) if(a[i][j]) x[i]^=x[j];//x[i]之后的解都已经确定,由此推出x[i]
    }
    return 0;
}
//枚举自由变元,以返回所有解的和最小为例
int enum_free(int n){//n个自由变元,n>0
    //状态压缩枚举
    int ret=INF,S=(1<<n);
    for(int S0=0;S0<S;S0++){
        int cnt=0;//统计所有解的和
        for(int j=0;j<n;j++){
            if((1<<j)&S0){
                x[free_x[j]]=1;
                cnt++;
            }
            else x[free_x[j]]=0;
        }
        //还剩var-n个变元
        for(int i=var-n-1;i>=0;i--){
            int idx;//当前要找非自由变元x[idx]
            for(idx=i;idx<var;idx++) if(a[i][idx]) break;//找到第i行第一个非0的元素
            x[idx]=a[i][var];
            for(int j=idx+1;j<var;j++) if(a[i][j]) x[idx]^=x[j];
            cnt+=x[idx];
        }
        ret=min(ret,cnt);
    }
    return ret;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值