pku 1222 EXTENDED LIGHTS OUT 解题报告 之高斯消去法

pku 1222 EXTENDED LIGHTS OUT 解题报告 之高斯消去法

之前我写了这题的解题报告,但并不是最优化的方法。有点像模拟的那种。还好,经过算法导论这个群上的牛人指导,现在我终于可以用到了最优的方法——高斯消去法。十分感谢那位牛人。年龄还比我小呢。要加油才行。感觉数学很强,很中用。自己在大一并没有疯狂学好数学,现在可真后悔。

思路:首先我应该想想,press一次(i,j)的灯,影响其上下左右的灯,这就好比就相当于对整个灯盘做了一次线性变换。想明白这一点十分重要(相信你能想明白~~~~(*^__^*) 嘻嘻……)~。那么我们就可以将结果30个假设为30个未知量x。输入的灯初始状态作为线性方程组右端的30个值,那么其线性方程组左端的系数怎么求呢?其实我已经说了,当press一次(ij)的灯,相当于一次线性变换,那么求出每个灯翻转一次,受影响的灯,即其他xi值,变换得:x1+···+xi=ansi)。

想明白这个数学模型,问题就很好解决。剩下就是套用高斯消去法的模板了。但要注意一点就是结果要%2

AC代码:

#include <iostream>

using namespace std;

#define N 30

bool fr[N];

int f[N][N], ans[N], x[N], n, t;

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

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

void set(int i, int r, int c)

{

       int j = r * 6 + c;

       f[i][j] = 1;

}

void init()

{

       int r, c;

       memset(f, 0, sizeof(f));

       for (int i = 0; i < n; i++)

       { //i块影响哪些

              r = i / 6, c = i % 6;

              set(i, r, c);

              if (r > 0)

              {

                     set(i, r + dr[0], c + dc[0]);

              }

              if (r < 4)

              {

                     set(i, r + dr[1], c + dc[1]);

              }

              if (c > 0)

              {

                     set(i, r + dr[2], c + dc[2]);

              }

              if (c < 5)

              {

                     set(i, r + dr[3], c + dc[3]);

              }

       }

}

int Guass()

{

       int t, i, j, k, temp;

       bool flag;

       for (t = 0; t < n; t++)   

       {

              if (f[t][t] == 0)

              {

                     flag = false;

                     for (i = t + 1; i < n; i++)

                     {

                            if (f[i][t] != 0)

                            {

                                   flag = true;

                                   for (k = 0; k < n; k++)

                                   {

                                          temp = f[t][k];

                                          f[t][k] = f[i][k];

                                          f[i][k] = temp;

                                   }

                                   temp = ans[t];

                                   ans[t] = ans[i];

                                   ans[i] = temp;

                                   break;

                            }

                     }

                     if (!flag)

                     {

                            return 1;

                     }

              }

              for (i = t + 1; i < n; i++)

              {

                     if (f[i][t] != 0)

                     {

                            temp = f[i][t];

                            for (j = t; j < n; j++)

                            {

                                   f[i][j] = (f[i][j] * f[t][t]) % 2;

                                   f[i][j] = (f[i][j] - f[t][j] * temp + 2) % 2;

                            }

                            ans[i] = (ans[i] * f[t][t] - ans[t] * temp + 2) % 2;

                     }

              }

       }

       for (i = n - 1; i >= 0; i--)

       {

              temp = ans[i];

              for (j = n - 1; j > i; j--)

              {

                     temp = (temp - f[i][j] * x[j] + 2) % 2;

              }

              x[i] = ((temp / f[i][i]) + 2) % 2;

       }

       return 0;

}

void print()

{

       for (int i = 0; i < n; i++)

       {

              int t = i % 6;

              if (t)

              {

                     printf(" ");

              }

              printf("%d", x[i]);

              if (t == 5)

              {

                     printf("/n");

              }

       }

}

int main()

{

       scanf("%d",&t);

       for (int q = 1; q <= t; q++)

       {

              n = 30;

              for (int i = 0; i < n; i++)

              {

                     scanf("%d", ans + i);

              }

              init();

              printf("PUZZLE #%d/n",q);

              Guass();

              print();

       }

       return 0;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值