高斯消元

高斯消元对应的矩阵有两种:

  • 常规的线性方程组
  • 异或操作(不需要乘上一个数再相减,直接异或即可)



    概念理解起来不太费力,重点是代码实现。

ACWing207. 球形空间产生器(点击访问)





这道题目重点是考察解线性方程组(不太好用暴力来进行解题)

使用解线性方程组来进行求解

求解思路

代码

#include <bits/stdc++.h>
using namespace std;
double a[20][20];
double c[20][20];
double b[20];
const float zero = 1e-8;
int main()
{
int n;
//扫描数据
cin >> n;
for(int i = 0; i <= n; i++)
for(int j = 1; j <= n; j++)
scanf("%lf", &a[i][j]);
//把其他的与第一个进行相减,然后得到线性增广炬阵(c是系数矩阵,b是增广炬阵)
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++) c[i][j] = 2 * a[i][j] - 2 * a[0][j];
for(int j = 1; j <= n; j++) b[i] += a[i][j] * a[i][j] - a[0][j] * a[0][j];
}
//进行消元
for(int i = 1; i <= n; i++)//第i个变量
{
int pos = i;
while(pos < n && fabs(c[pos][i]) < zero) pos ++;
for(int j = 1; j <= n; j++) swap(c[pos][j], c[i][j]);
swap(b[pos], b[i]);
for(int j = 1; j <= n; j++)
{
if(j == pos) continue;
if(fabs(c[j][i]) > zero)
{
double factor = c[j][i] / c[pos][i];
for(int k = 1; k <= n; k++)
{
c[j][k] -= factor*c[pos][k];
}
b[j] -= factor * b[pos];
}
}
}
for(int i = 1; i <= n; i++)
{
double ans = b[i] / c[i][i];
printf("%.3lf ", ans);
fflush(stdout);
}
return 0;
}

ACWing208. 开关问题(点击访问)



思路

暴力枚举显然是不行。

如果直接取思考解决问题的方法,不太可能。这时候应该与数学相联系。

  • 开关的状态只有0,1两种
  • 开关打开关闭状态可以用0或者1来进行表示,同时,操作也可以这样。

同时,容易发现:最终操作的结果与按压开关的次序并没有关系。

可以存放一个矩阵

(\(A[i][j]==1,则操作第i个开关会影响第j个开关。特别让A[i][i]==1\))

根据上面的矩阵,操作某一个开关,这个开关可以看做是一个代号,转化为操作矩阵里面的对应元素。

回顾:(依据矩阵自由元的个数,还可以判断具体有多少种情况)

在矩阵中,如果最终的系数矩阵是单位矩阵,说明有一种情况。

如果有一行系数矩阵全部为0,但是这一行的常数矩阵是1,那么就无解

如果有m行全部都是0,那么就有m个自由变元(最终解的个数就是\(2^m\))

代码

#include <bits/stdc++.h>
using namespace std;
int matrix[40];
void Init()
{
memset(matrix, 0, sizeof(matrix));
}
inline int col(int x)
{
return 1 << x;
}
int main()
{
int T;
cin >> T;
while(T--)
{
Init();//一定不要忘记初始化
int cnt = 0;
bool logical = true;
int n;
cin >> n;
for(int k = 1; k <= 2; k++)
for(int i = 1; i <= n; i++)
{
int tmp = 0;
scanf("%d", &tmp);
matrix[i] ^= tmp;
}
while(1)
{
int x, y; scanf("%d%d", &x, &y);
if(!(x||y)) break;
matrix[y] = matrix[y] | col(x);
}
for(int i = 1; i <= n; i++)
{
matrix[i] |= col(i);
}
int last = 0;
for(int i = 1; i <= n; i++)
{
int pos = last+1;
while(pos <= n &&( (matrix[pos]>>i) & 1)==0)
pos++;
if(pos > n) continue;
last = pos;
swap(matrix[pos], matrix[last]);
for(int j = 1; j <= n; j++)
{
//一定要排除第pos行
if(j==last) continue;
if((matrix[j] & col(i)))
{
matrix[j] ^= matrix[last];
}
}
}
for(int i = 1; i <= n; i++)
{
if((matrix[i] >> 1) == 0 && matrix[i] != 0)
{
logical = false;
break;
}
if(!matrix[i]) cnt++;
} if(logical)
{
if(cnt==0) printf("1\n");
else{
int ans = 1 << cnt;
printf("%d\n", ans);
}
}
else
{
printf("Oh,it's impossible~!!\n");
}
}
return 0;
}

总结

答案控制:不需要设置太多的标志,一个ans就够了

if(logical)
{
if(cnt==0) printf("1\n");
else{
int ans = 1 << cnt;
printf("%d\n", ans);
}
}
else
{
printf("Oh,it's impossible~!!\n");
}
  • 默认ans = 1;
  • 如果不可行,就让ans = 0;
  • 如果有一个自由元,那么就让ans << 1;

欣赏

int last = 0;//需要给一个last
//以防万一
/*
1 0 0 1
0 0 1 0
0 0 1 0
这种情况
*/
for(int i = 1; i <= n; i++)//如果行数和列数不相等,还是选取较大的为好
{
int pos = last+1;
while(pos <= n &&( (matrix[pos]>>i) & 1)==0)
pos++;
if(pos > n) continue;
last = pos;
swap(matrix[pos], matrix[last]);
for(int j = 1; j <= n; j++)
{
//一定要排除第last行
if(j==last) continue;
if((matrix[j] & col(i)))
{
matrix[j] ^= matrix[last];
}
}
}

线性空间

定义

线性空间是一个向量集合,并且关于一下两个运算封闭:

  1. 向量加法
  2. 向量数乘

如果一个向量可以被若干个向量通过向量乘法以及向量加法表示,那么就称这个向量可以被这几个向量线性标出。

线性空间的产生方法:\(a_1,a_2.....a_k\)所能表示的所有向量所构成的集合。

\(a_1,a_2.....a_k\)被称作生成子集。

线性相关&线性无关

任意在向量空间中选出若干个向量,如果某个向量可以被其他向量所表示,那么这些向量线性相关。否则线性无关。

线性空间的基底(基)

  • 定义一:线性无关的生成子集;
  • 定义二:极大线性无关子集。

线性空间的维数:一个线性空间的所有基包含的向量个数相等,成为维数。

对于一个m行n列的矩阵,如果把每一行看做是长度为m向量(行向量)。这n个向量所能表示的所有向量组成一个线性空间,线性空间的维数就称为矩阵的行秩。同理有列秩。

易知矩阵的行秩等于列秩。统称为秩。

在对一个矩阵进行化简之后,所有的非零行向量就是一个基(初等行变换不改变这些行向量所能表示的线性空间)个数就是矩阵的秩。


ACWing209. 装备购买





对于所有装备,把一个装备看成是m维的向量。总体看成是n*m矩阵,然后求出矩阵的基底就行(但是要注意有一个贪心,在每一次选择系数非零的矩阵的时候,应该选择对应的价钱是最小的)

代码

在参考标准代码之后所写的代码:

#include <bits/stdc++.h>
using namespace std;
//注意矩阵运算需要使用double型
#define N 510
long double matrix[N][N];
long double price[N];
const long double eps = 1e-8;
int main()
{
long double ans = 0;
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
scanf("%Lf", &matrix[i][j]);
for(int i = 1; i <= n; i++)
scanf("%Lf", &price[i]);
int dim = 0;//dim表示基底的数量(和我之前的last相似)
for(int i = 1; i <= m; i++)
{
int now = 0;
for(int j = dim+1; j<= n; j++)
{
if(fabs(matrix[j][i]) > eps && (now==0 || price[j] < price[now]))
now = j;
}
if(now==0)//说明这一个元素是自由元。
continue;
dim++;
ans += price[now];
for(int j = 1; j <= m; j++)
swap(matrix[dim][j], matrix[now][j]);
swap(price[now], price[dim]);
for(int j = 1; j <= n; j++)
if(fabs(matrix[j][i]) > eps && j != dim)
{
long double rate = matrix[j][i] / matrix[dim][i];
for(int k = 1; k <= m; k++)
{
matrix[j][k] -= matrix[dim][k] * rate;
}
}
}
printf("%d %.0Lf", dim, ans);
return 0;
}

总结:

这道题目失误的地方有两个:

一个是浮点类型的数字在和其他数字进行比较大小的时候,我没有加fabs,导致错误。

还有一点就这道题目卡了double

是在以后所有的数据中,我尽量采用long double来进行运算。

long double的注意事项:

  • 输入应该采用%Lf
  • 输出应该使用%Lf
  • 对应的函数使用fabsl(),cosl()等等。
#include <bits/stdc++.h>
using namespace std;
//注意矩阵运算需要使用double型
#define N 510
long double matrix[N][N];
long double price[N];
const long double eps = 1e-8;
int main()
{
long double ans = 0;
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
scanf("%Lf", &matrix[i][j]);
for(int i = 1; i <= n; i++)
scanf("%Lf", &price[i]);
int dim = 0;//dim表示基底的数量(和我之前的last相似)
for(int i = 1; i <= m; i++)
{
int now = 0;
for(int j = dim+1; j<= n; j++)
{
if(fabs(matrix[j][i]) > eps && (now==0 || price[j] < price[now]))
now = j;
}
if(now==0)//说明这一个元素是自由元。
continue;
dim++;
ans += price[now];
for(int j = 1; j <= m; j++)
swap(matrix[dim][j], matrix[now][j]);
swap(price[now], price[dim]);
for(int j = 1; j <= n; j++)
if(fabs(matrix[j][i]) > eps && j != dim)
{
long double rate = matrix[j][i] / matrix[dim][i];
for(int k = 1; k <= m; k++)
{
matrix[j][k] -= matrix[dim][k] * rate;
}
}
}
printf("%d %.0Lf", dim, ans);
return 0;
}

AcWing210. 异或运算



思路:注意线性空间的推广!

异或与线性空间具有一致性

你可以从中选取一些(至少一个)进行异或(xor)运算,从而得到很多不同的结果。

这句话提示了讨论的范围是在把数字当做向量以后所得到的异或空间。

可以通过消元来把复杂的问题变得清晰易懂。

DEBUG总结

  1. 对于位运算,判断某一位是不是1的办法

    (x>>i)&1
  2. 这道题目我万万没有想到:

    竟然如果矩阵的秩等于总的行数,那么对于所给的这n个数字,无论如何也整不出一个0来。

    但是如果dim小于总的行数,那么这n个向量是线性无关的。所以可以取大于等于1个数字,把他们给消去。
#include <bits/stdc++.h>
using namespace std;
#define N 10010
long long matrix[N];
int main()
{
int sddsdsafdasg = 1;
int T;
cin >> T;
while(T--)
{
printf("Case #%d:\n", sddsdsafdasg++);
int dim = 0;
int n, m;
cin >> n;
for(int i = 1; i <= n; i++) scanf("%lld", &matrix[i]);
/*进行高斯消元*/
for(int i = 63; i >= 0; i--)
{
int now = 0;
for(int j = dim+1; j <= n; j++)
if(((matrix[j] >> i) & 1) != 0)
{
now = j;
break;
}
if(now == 0) continue;
dim++;
swap(matrix[dim], matrix[now]);
for(int j = 1; j<= n; j++)
{
if( ((matrix[j]>>i) & 1) == 1 && j!=dim)
matrix[j] ^= matrix[dim];
} }
/*高斯消元完成*/
scanf("%d", &m);
for(int t = 1; t <= m; t++)
{
int base = 1;
if(dim >= n) base = 0;
int cnt = dim;
long long ans = 0;
long long q;
scanf("%lld", &q);
q-=base;
if((unsigned long long)q >= (1LL << dim))
{
printf("-1\n");
continue;
}
while(q)
{
if(q&1)
ans ^= matrix[cnt];
q >>= 1;
cnt--;
}
printf("%lld\n", ans);
}
}
return 0;
}

算法竞赛进阶指南0x35高斯消元与线性空间的更多相关文章

  1. 0x35 高斯消元与线性空间

    颓了十天回来做题果然…… 感觉还是很有收获的,这两以前都没学过 bzoj1013: [JSOI2008]球形空间产生器sphere poj1830(upd) 之前做得很烂还被 D飞*2 了..重做一次 ...

  2. 《算法竞赛进阶指南》0x10 基本数据结构 Hash

    Hash的基本知识 字符串hash算法将字符串看成p进制数字,再将结果mod q例如:abcabcdefg 将字母转换位数字(1231234567)=(1*p9+2*p8+3*p7+1*p6+2*p5 ...

  3. 《算法竞赛进阶指南》1.4Hash

    137. 雪花雪花雪花 有N片雪花,每片雪花由六个角组成,每个角都有长度. 第i片雪花六个角的长度从某个角开始顺时针依次记为ai,1,ai,2,-,ai,6. 因为雪花的形状是封闭的环形,所以从任何一 ...

  4. bzoj 1787 && bzoj 1832: [Ahoi2008]Meet 紧急集合(倍增LCA)算法竞赛进阶指南

    题目描述 原题连接 Y岛风景美丽宜人,气候温和,物产丰富. Y岛上有N个城市(编号\(1,2,-,N\)),有\(N-1\)条城市间的道路连接着它们. 每一条道路都连接某两个城市. 幸运的是,小可可通 ...

  5. POJ1639 算法竞赛进阶指南 野餐规划

    题目描述 原题链接 一群小丑演员,以其出色的柔术表演,可以无限量的钻进同一辆汽车中,而闻名世界. 现在他们想要去公园玩耍,但是他们的经费非常紧缺. 他们将乘车前往公园,为了减少花费,他们决定选择一种合 ...

  6. 算法竞赛进阶指南 0x00 基本算法

    放在原来这个地方不太方便,影响阅读体验.为了读者能更好的刷题,另起一篇随笔. 0x00 基本算法 0x01 位运算 [题目][64位整数乘法] 知识点:快速幂思想的灵活运用 [题目][最短Hamilt ...

  7. 算法竞赛进阶指南--快速幂,求a^b mod p

    // 快速幂,求a^b mod p int power(int a, int b, int p) { int ans = 1; for (; b; b >>= 1) { if (b &am ...

  8. 算法竞赛进阶指南 0x50 总论

    目录 AcWing895. 最长上升子序列 方法一 方法二 当询问最长子序列是哪些的时候 896. 最长上升子序列 II 思路 O(NlogN)做法:贪心+二分 代码 AcWing\897. 最长公共 ...

  9. 算法竞赛进阶指南0x41并查集

    并查集简介 并查集的两类操作: Get 查询任意一个元素是属于哪一个集合. Merge 把两个集合合并在一起. 基本思想:找到代表元. 注意有两种方法: 使用一个固定的值(查询方便,但是在合并的时候需 ...

随机推荐

  1. Open Harmony移植:build lite配置目录全梳理

    摘要:本文主要介绍build lite 轻量级编译构建系统涉及的配置目录的用途,分析相关的源代码. 本文分享自华为云社区<移植案例与原理 - build lite配置目录全梳理>,作者:z ...

  2. ansible模块的介绍与使用

    ansible-doc的使用 1.ansible-doc -h可以看见ansible-doc的所有参数 2.ansible-doc 命令格式:ansible-doc [-l|-F|-s] [optio ...

  3. 项目完成小结 - Django3.x版本 - 开发部署小结 (2)

    前言 好久没更新博客了,最近依然是在做之前博客说的这个项目:项目完成 - 基于Django3.x版本 - 开发部署小结 这项目因为前期工作出了问题,需求没确定好,导致了现在要做很多麻烦的工作,搞得大家 ...

  4. 拥有webkit内核浏览器的平台

    1-拥有webkit内核的浏览器: IOS safari Android 的浏览器 Google chrome 猎豹浏览器 百度浏览器 Opera 以上可知手机的浏览器均为webkit内核 2-拥有其 ...

  5. 关于『进击的Markdown』:第三弹

    关于『进击的Markdown』:第三弹 建议缩放90%食用 我与神明画押,赌这弹markdown又双叒叕拖稿了 %%%Markdown!我的CSDN编辑器崩了呜呜呜 各路英雄豪杰,大家好!  我们要开 ...

  6. npm错误:Cannot find module ‘compression-webpack-plugin

    转自 (82条消息) 前端开发遇到Cannot find module 'compression-webpack-plugin'问题解决_brave_zhao的博客-CSDN博客 <div id ...

  7. Node.js安装与环境配置

    废话不多少直接上干货.坐车扶稳, 当然你要知道Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台.其次Node.js是一个事件驱动I/O服务端JavaScript环境, ...

  8. bare Git 仓库是什么?

    背景 今天,坐我旁边的同事问我一些关于服务器上命令的问题.其中有一个用了特殊参数的 git init 的命令,我也不认识,遂去 Google... bare Git 仓库 定义 A bare Git ...

  9. vue 使用npm install安装依赖失败 【问题分析与解决】

    1 进入项目根目录,先通过 npm install 命令安装项目所需依赖,再通过 vue ui 命令打开 Vue Cli 提供的图形化界面,选择项目所在文件夹将项目导入. 出现问题 npm insta ...

  10. BUUCTF-菜刀666

    菜刀666 这题和之前做过的流量题不同,对我还是有些难度.看了看大佬的wp才做出来的 wireshark打开流量包,一开始只是单纯过滤http,包很多,看花了眼,看了好多也没觉得有啥异常. 后面才知道 ...