算法竞赛进阶指南0x35高斯消元与线性空间
高斯消元
高斯消元对应的矩阵有两种:
- 常规的线性方程组
- 异或操作(不需要乘上一个数再相减,直接异或即可)
概念理解起来不太费力,重点是代码实现。
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];
}
}
}
线性空间
定义
线性空间是一个向量集合,并且关于一下两个运算封闭:
- 向量加法
- 向量数乘
如果一个向量可以被若干个向量通过向量乘法以及向量加法表示,那么就称这个向量可以被这几个向量线性标出。
线性空间的产生方法:\(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的办法
(x>>i)&1
- 这道题目我万万没有想到:
竟然如果矩阵的秩等于总的行数,那么对于所给的这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高斯消元与线性空间的更多相关文章
- 0x35 高斯消元与线性空间
颓了十天回来做题果然…… 感觉还是很有收获的,这两以前都没学过 bzoj1013: [JSOI2008]球形空间产生器sphere poj1830(upd) 之前做得很烂还被 D飞*2 了..重做一次 ...
- 《算法竞赛进阶指南》0x10 基本数据结构 Hash
Hash的基本知识 字符串hash算法将字符串看成p进制数字,再将结果mod q例如:abcabcdefg 将字母转换位数字(1231234567)=(1*p9+2*p8+3*p7+1*p6+2*p5 ...
- 《算法竞赛进阶指南》1.4Hash
137. 雪花雪花雪花 有N片雪花,每片雪花由六个角组成,每个角都有长度. 第i片雪花六个角的长度从某个角开始顺时针依次记为ai,1,ai,2,-,ai,6. 因为雪花的形状是封闭的环形,所以从任何一 ...
- bzoj 1787 && bzoj 1832: [Ahoi2008]Meet 紧急集合(倍增LCA)算法竞赛进阶指南
题目描述 原题连接 Y岛风景美丽宜人,气候温和,物产丰富. Y岛上有N个城市(编号\(1,2,-,N\)),有\(N-1\)条城市间的道路连接着它们. 每一条道路都连接某两个城市. 幸运的是,小可可通 ...
- POJ1639 算法竞赛进阶指南 野餐规划
题目描述 原题链接 一群小丑演员,以其出色的柔术表演,可以无限量的钻进同一辆汽车中,而闻名世界. 现在他们想要去公园玩耍,但是他们的经费非常紧缺. 他们将乘车前往公园,为了减少花费,他们决定选择一种合 ...
- 算法竞赛进阶指南 0x00 基本算法
放在原来这个地方不太方便,影响阅读体验.为了读者能更好的刷题,另起一篇随笔. 0x00 基本算法 0x01 位运算 [题目][64位整数乘法] 知识点:快速幂思想的灵活运用 [题目][最短Hamilt ...
- 算法竞赛进阶指南--快速幂,求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 ...
- 算法竞赛进阶指南 0x50 总论
目录 AcWing895. 最长上升子序列 方法一 方法二 当询问最长子序列是哪些的时候 896. 最长上升子序列 II 思路 O(NlogN)做法:贪心+二分 代码 AcWing\897. 最长公共 ...
- 算法竞赛进阶指南0x41并查集
并查集简介 并查集的两类操作: Get 查询任意一个元素是属于哪一个集合. Merge 把两个集合合并在一起. 基本思想:找到代表元. 注意有两种方法: 使用一个固定的值(查询方便,但是在合并的时候需 ...
随机推荐
- Docker被禁了!只能靠它了......
科技飞速发展的今天,企业对候选人有了新的更高要求,如市场.运营等必须会Python.Sql,面试常问诸如用户漏斗等考察数据分析能力.可以说,懂数据的人会更有竞争力通过面试. 而市场上,专业的数据分析人 ...
- fedora访问win10共享
sudo mount -t cifs -o username=user,password=123 //192.168.31.20/aa /home/liao/win
- SpringBoot项目使用jasypt加解密
Jasypt 是一个 Java 库,它允许开发者以最小的努力为他 / 她的项目添加基本的加密功能,而且不需要对密码学的工作原理有深刻的了解. 一.添加依赖 <dependency> < ...
- 292. Nim Game - LeetCode
Question 292. Nim Game Solution 思路:试着列举一下,就能发现一个n只要不是4的倍数,就能赢. n 是否能赢 1 true 2 true 3 true 4 false 不 ...
- column-文本对齐输出
文本输出对齐,可以指定对应的分隔符,将上下文的字符串按分隔符列对齐. 语法 column [选项] 选项 -s 设置分隔符,默认为空格. -t 判断输入的列数来创建一个表,使列对齐. -c 设置显示的 ...
- 安装Net-Tools到CentOS(YUM)
Net-Tools是一个Linux系统中基本的网络工具集,其集成了常用的网络管理命令"ifconfig.netstat.arp.route等". 运行环境 系统版本:CentOS ...
- 每天一个 HTTP 状态码 206
206 Partial Content 206 Partial Content 是当客户端请求时使用了 Range 头部,服务器端回复的响应,表示只响应一部分内容. 实例 请求: GET /favor ...
- 关于『HTML』:第二弹
关于『HTML』:第二弹 建议缩放90%食用 第二弹! 它来了! 它来了! 我竟然没有拖更,对了,你们昨天用草稿纸了么 开始正文之前提一个问题:大家知道"%%%"是什么意思吗?就这 ...
- 关于『HTML』:第一弹
关于『HTML』:第一弹 建议缩放90%食用 根据C2024XSC212童鞋的提问, 我准备写一稿关于『HTML』基础的帖 But! 当我看到了C2024XSC130的 "关于『HTML5』 ...
- ElasticSearch7.3学习(三十)----ES7.X SQL新特性解析及使用Java api实现sql功能
一.ES7 sql新特性 1.1 数据准备 创建索引及映射 建立价格.颜色.品牌.售卖日期 四个字段 PUT /tvs PUT /tvs/_mapping { "properties&quo ...