poj 1222 高斯消元详解
来源:互联网 发布:阿里云ace下线原因 编辑:程序博客网 时间:2024/05/18 00:45
题意
有一个5 * 6的矩阵,每个位置表示灯,1表示灯亮,0表示灯灭。
然后如果选定位置i,j点击,则位置i,j和其上下左右的灯的状态都会反转。
现在要你求出一个5 * 6的矩阵,1表示这个灯被点击过,0表示没有。
要求这个矩阵能够使得原矩阵的灯全灭。
非诚勿扰。
解析
来源
首先,要明确一些问题:
1.这些灯点击的顺序可以是任意的;
2.每只灯只能被点击1次,因为点击2次相当于没点。
然后,以3*3的矩阵为例,假设初始灯泡的布局:
每次点击某灯泡的时候,可以看成是在原矩阵上异或上一个如下的矩阵:
比如点击2,2点:
再如点击1,1点:
然后,假设X(i, j)表示使得L变为0矩阵的情况下,位置i,j上的灯泡点击不点击。0表示不点击,1表示点击。
所以可以列出如下的方程:
方程两边同时异或上L,得:
用at, bt,ct…来表示A(i, j)的元素,lt来表示L的第t个元素。
即:
则将上述矩阵展开,可得如下方程组:
只要求解这个方程组中的9个x的解,就可以得到使得原矩阵全灭的矩阵了。
现在的问题是如何求解上述方程组。
先复习一下线代当中的高斯消元法:
高斯消元解线性方程组
高斯消元解普通加减方程组模板O(n^3):
int a[maxn][maxn]; //增广矩阵int x[maxn]; //解集bool freeX[maxn]; //标记解是否是自由变元int gcd(int a, int b){ return b ? gcd(b, a % b) : a;}int lcm(int a, int b){ return a / gcd(a, b) * b;}//高斯消元解方程组//返回值-2表示有浮点数解,无整数解//返回值-1表示无解,0表示有唯一解,大于0表示有无穷解,返回自由变元个数//有equ个方程,var个变元//增广矩阵行数[0, equ - 1]//增广矩阵列数[0, var]int gauss(int equ, int var){ for (int i = 0; i <= var; i++) { x[i] = 0; freeX[i] = true; } //转换为阶梯矩阵 //col表示当前正在处理的这一列 int col = 0; int row = 0; //maxR表示当前这个列中元素绝对值最大的行 int maxRow; for (; row < equ && col < var; row++, col++) { //枚举当前正在处理的行 //找到该col列元素绝对值最大的那行与第k行交换 maxRow = row; for (int i = row + 1; i < equ; i++) { if (abs(a[maxRow][col]) < abs(a[i][col])) { maxRow = i; } } if (maxRow != row) { //与第row行交换 for (int j = row; j < var + 1; j++) { swap(a[row][j], a[maxRow][j]); } } if (a[row][col] == 0) { //说明该col列第row行以下全是0,处理当前行的下一列 row--; continue; } for (int i = row + 1; i < equ; i++) { //枚举要删的行 if (a[i][col] != 0) { int LCM = lcm(abs(a[i][col]), abs(a[row][col])); int ta = LCM / abs(a[i][col]); int tb = LCM / abs(a[row][col]); //异号 if (a[i][col] * a[row][col] < 0) tb = -tb; for (int j = col; j < var + 1; j++) { a[i][j] = a[i][j] * ta - a[row][j] * tb; } } } }// //1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0). for (int i = row; i < equ; i++) { if (a[i][col] != 0) { return -1; } } // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵. // 出现的行数即为自由变元的个数. if (row < var) { // 首先,自由变元有var - k个,即不确定的变元至少有var - k个. for (int i = row - 1; i >= 0; i--) { // 第i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第k行到第equ行. // 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的. // freeNum用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元. int freeNum = 0; int freeIndex = 0; for (int j = 0; j < var; j++) { if (a[i][j] != 0 && freeX[j]) { freeNum++; freeIndex = j; } } if (1 < freeNum)// 无法求解出确定的变元. continue; // 说明就只有一个不确定的变元freeIndex,那么可以求解出该变元,且该变元是确定的. int tmp = a[i][var]; for (int j = 0; j < var; j++) { if (a[i][j] != 0 && j != freeIndex) { tmp -= a[i][j] * x[j]; } } x[freeIndex] = tmp / a[i][freeIndex]; freeX[freeIndex] = false; } return var - row; } // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵. // 计算出Xn-1, Xn-2 ... X0. for (int i = var - 1; i >= 0; i--) { int tmp = a[i][var]; for (int j = i + 1; j < var; j++) { if (a[i][j] != 0) { tmp -= a[i][j] * x[j]; } } if (tmp % a[i][i] != 0)//浮点数 return -2; x[i] = tmp / a[i][i]; } return 0;}
然后就这题如何用高斯消元来解决了。(╯^╰)
首先是将A(i,j)构造成增广矩阵。
然后是在高斯消元的时候把原来的+-操作改成异或的情形。
代码
#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <climits>#include <cassert>#define LL long longusing namespace std;const int inf = 0x3f3f3f3f;const double eps = 1e-8;const double pi = 4 * atan(1.0);const double ee = exp(1.0);const int maxn = 100 + 10;int a[maxn][maxn]; //增广矩阵int x[maxn]; //解集bool freeX[maxn]; //标记解是否是自由变元int gcd(int a, int b){ return b ? gcd(b, a % b) : a;}int lcm(int a, int b){ return a / gcd(a, b) * b;}//高斯消元解方程组//返回值-2表示有浮点数解,无整数解//返回值-1表示无解,0表示有唯一解,大于0表示有无穷解,返回自由变元个数//有equ个方程,var个变元//增广矩阵行数[0, equ - 1]//增广矩阵列数[0, var]int gauss(int equ, int var){ for (int i = 0; i <= var; i++) { x[i] = 0; freeX[i] = true; } //转换为阶梯矩阵 //col表示当前正在处理的这一列 int col = 0; int row = 0; //maxR表示当前这个列中元素绝对值最大的行 int maxRow; for (; row < equ && col < var; row++, col++) { //枚举当前正在处理的行 //找到该col列元素绝对值最大的那行与第k行交换 maxRow = row; for (int i = row + 1; i < equ; i++) { if (abs(a[maxRow][col]) < abs(a[i][col])) { maxRow = i; } } if (maxRow != row) { //与第row行交换 for (int j = row; j < var + 1; j++) { swap(a[row][j], a[maxRow][j]); } } if (a[row][col] == 0) { //说明该col列第row行以下全是0,处理当前行的下一列 row--; continue; } for (int i = row + 1; i < equ; i++) { //枚举要删的行 if (a[i][col] != 0) { for (int j = col; j < var + 1; j++) { /// a[i][j] ^= a[row][j]; } } } } for (int i = var - 1; i >= 0; i--) { x[i] = a[i][var]; for (int j = i + 1; j < var; j++) { /// x[i] ^= (a[i][j] && x[j]); } } return 0;}int main(){#ifdef LOCAL freopen("in.txt", "r", stdin);#endif // LOCAL int ncase; int ca = 1; scanf("%d", &ncase); while (ncase--) { memset(a, 0, sizeof(a)); for (int i = 0; i < 30; i++) { scanf("%d", &a[i][30]); } for (int i = 0; i < 5; i++) { for (int j = 0; j < 6; j++) { int t = i * 6 + j; a[t][t] = 1; if(i>0)a[(i-1)*6+j][t]=1; if(i<4)a[(i+1)*6+j][t]=1; if(j>0)a[i*6+j-1][t]=1; if(j<5)a[i*6+j+1][t]=1; } } gauss(30, 30); printf("PUZZLE #%d\n", ca++); for(int i = 0; i < 30; i++) { printf("%d", x[i]); if((i+1) % 6 == 0) printf("\n"); else printf(" "); } } return 0;}
UPD:感谢@GoldenGate灰灰 指出模板中的错误。
0 0
- poj 1222 高斯消元详解
- poj 1611(详解)
- poj 1942(详解)
- poj 3252(详解)
- poj 2230详解
- poj 1222 (高斯消元)
- POJ 1222 高斯消元
- poj 1222 高斯消元
- 【高斯消元】poj 1222
- poj 1222 高斯消元
- Poj 1222 高斯消元
- POJ 1222 高斯消元
- poj-1222 高斯消元
- poj 1222 高斯消元
- poj 1222(高斯消元)
- poj 3581 后缀数组 详解
- POJ 1195 树状数组详解
- POJ 3461 Oulipo(KMP详解)
- java基础第八讲——URL、XML的解析
- logistic回归与牛顿方法的python实现 (standford公开课程小作业)
- 黑马程序员---iOS基础---协议
- UITextField总结
- Java删除ArrayList中的重复元素的2种方法
- poj 1222 高斯消元详解
- URL
- Swap Nodes in Pairs
- 数据源架构模式之行入口模式
- webkit编译
- php模拟post请求发送文件
- 深度学习的"非主流"应用
- Android之Inflate()方法用途
- hdu1716(排列2)