首页 > 编程语言 >算法思想

算法思想

时间:2023-04-09 17:11:29浏览次数:46  
标签:cnt le 思想 int mid 算法 数组 mathcal

\(\mathcal{Part}\) 1. 前提提要

注意:本文为提高组难度的算法思想,主要为前缀和,差分等优化

因为是思想,讲的会比较玄乎,能理解就好

\(\mathcal{Part}\) 2. 双指针

双指针通常解决区间问题

步骤是,确定一个右节点或左节点作为一个参考点,通常取右节点,记为 \(j\)

我们考虑一个刚好符合题目条件的最小节点,记为 \(l\)

双指针有个前提:当右指针往右移动时,左指针不可能往前走

解决步骤:

右指针往右跳,让左指针也往右跳,跳到符合条件为止,这就是题目要求的以 \(r\) 为右端点最长的区间

因为左指针和右指针都只会跳 \(n\) 次,总时间复杂度为 \(\mathcal{O}(n)\)

\(\mathcal{Practice}\) 1.

P1638逛画展

\(\mathcal{Practice}\) 2.

P1102 A-B数对

\(\mathcal{Part}\) 3. 前缀和

一维前缀和

我们有一个序列 \(A\),我们要进行一下操作

  • \(\mathcal{O}(n)\) 地预处理
  • 给定一个 \(l\) 和 \(r\),求 \(\sum_{l\le i\le r} A_i\),时间复杂度为 \(\mathcal{O}(1)\)

我们记一个数组 \(B\) 满足

\[B_i=\sum_{1\le j\le i} A_j \]

预处理 \(B\) 数组,时间复杂度为 \(\mathcal{O}(n)\)

我们要查询 \([l,r]\) 的值,也就是 \(\sum_{l\le i\le r} A_i\),经过简单数学推导,如下

\[\begin{aligned} \sum_{l\le i\le r}A_i&=\sum_{1\le j\le r}A_j - \sum_{1\le k <l}A_k\\ &=B_r-B_{l-1} \end{aligned} \]

通俗点来说,就是 \([l,r]\) 的值等于 \(B_r-B_{l-1}\)

前缀和在众多领域起着重大作用,以代码量小和快著称

弊端是:不能动态修改

二维前缀和

给定一个二维序列,记为 \(A\),现要支持

  • 给定两点 \((l_1,r_1)\) 和 \((l_2,r_2)\),求以两点为矩形端点的矩形,里面所有数之和

记一个二维数组 \(B_{i,j}\) 为 \((1,1)\) 到 \((i,j)\) 的数之和

初始化 \(B_{i,j}=B_{i-1,j}+B_{i,j-1}-B_{i-1,j-1}+A_{ij}\)

证明平凡,自己画图

则两点 \((l_1,r_1)\) 和 \((l_2,r_2)\),求以两点为矩形端点的矩形,里面所有数之和就为:

\[B_{l2,r2}-B_{l1-1,r2}-B_{l2,r1-1}+B_{l1-1,r2-1} \]

推导过程平凡,请自己画图证明

多维前缀和

类比容斥原理

\(\mathcal{Practice}\)

见题单

\(\mathcal{Part}\) 4. 差分

一维差分

现有一个序列 \(A\),先要支持

  • 动态修改 \([l,r]\) 的值
  • \(O(n)\) 打印 \(A\) 序列的每个数

记一个数组 \(B\) 满足

\[B_i=A_i-A_{i-1} \]

我们看看 \(A_i\) 等与什么

\[\sum_{1\le j \le i}B_j=\sum_{1\le j\le i}A_j-A_{j-1}=A_i \]

也就是说,差分的前缀和等于原数组,简单的想,前缀和的差分等于差分

这三者就有机的连在一起了

接下来看怎么修改,记修改的数为 \(k\)

  • 当 \(i=l\) 时,\(B_i'=(A_i+k)-A_{i-1}=B_i+k\)
  • 当 \(i = r+1\) 时, \(B_i'=(A_{r+1}-(A_{r}+k))=B_i-k\)
  • 当 \(i\in (l,r]\) 时,\(B_i'=(A_i+k)-(A_{i-1}+k)=B_i\)

通俗点说,就是修改 \([l,r]\) 的值时,只需要修改 \(B_l\) 和 \(B_{r+1}\) 的值即可

时间复杂度为:\(\mathcal{O}(1)\)

二维差分

类比 \(S_{i,j}=S_{i-1,j}+S_{i,j-1}-S_{i-1,j-1}+A_{ij}\)

因为 \(B\) 的前缀和为 \(A\),则用 \(A\) 替代 \(S\),\(B\) 替代 \(A\) 则

\[A_{i,j}=A_{i-1,j}+A_{i,j-1}-A_{i-1,j-1}+B_{i,j}\\ B_{ij}=A_{i,j}-A_{i-1,j}-A_{i,j-1}+A_{i-1,j-1} \]

这就是二维差分的初始化了

我们考虑修改 \((u,v)\) 到 \((n,n)\) 的差分

  • 当 \((i,j) = (u,v)\) 时,\(B'_{i,j}=(A_{i,j}+w)-A_{i-1,j}-A_{i,j-1}+A_{i-1,j-1}=B_{i,j}+w\)

可以证明,其他位置的差分值不变

可以推导 \((u,v)\) 到 \((x,y)\) 的差分值为

\[B_{u,v}+w,B_{u,y+1}-w,B_{x+1,v}-w,B_{x+1,y+1}+w \]

证明平凡,类似后缀和

多维差分

和二维差分的推导过程一样

\(\mathcal{Practice}\)

见题单

\(\mathcal{Part}\) 5.离散化

我们要将一个值域为 \((-\infty,\infty)\) 的数映射到 \((0,n]\) 之中

怎么做到呢?

我们知道,映射有个性质

  • 每个数的大小关系不变

想到什么?排名是不是可以完美做到这一点?

所以,我们第一步是将原数组去重排序+排序

排序用 sort 人尽皆知,去重用什么呢?

使用 unique 函数,使用格式: unique(a.begin(),a.end())这里 a.begin(),a.end() 分别指数组的头指针和尾指针,普通数组用 a+1,a+1+n

  • 注意一点:该函数的返回值是不属于去重数组的第一个地址,比如有个数组长度为 \(n\) ,去重数组长度为 \(k\) ,他的返回值就是 \(a_{k+1}\)的地址。

根据上面几点,我们可以推出公式。

k=unique(a+1,a+1+n)-(a+1)

这样我们就可以求出去重数组的长度了。

第二步,我们求排名

设这个数为 \(A_i\),\(B\) 数组的排名为 \(k\),则他在 \(A\) 数组的排名为 \(k\)

那怎么找 \(i\) 呢?这里使用一个函数:

lower_bound(a+1,a+1+n,x) 他返回的是第一个大于等于 \(x\) 的元素的地址,注意:必须有序

于是,我们可以推出公式:

rk[i] = lower_bound(b + 1, b + 1 + k, a[i]) - b;

到此结束

因为 \(rk_i\) 存的是第 \(a_i\) 在 \(b\) 数组的下标,则

\[B_{rk_i}=A_i \]

sort(b + 1, b + 1 + n);
int *ed = unique(b + 1, b + 1 + n);
for (int i = 1; i <= n; i++) {
    rk[i] = lower_bound(b + 1, ed, a[i]) - (b + 1) + 1;
    cout << rk[i] << ' ';
}

\(\mathcal{Practice}\)

见题单

\(\mathcal{Part}\) 6. 位运算

基本位运算

  • 按位与:\(x\&y\),把两数的对应位做逻辑与
  • 按位或:\(x|y\),把两位的对应位做逻辑或
  • 按位异或:\(x\oplus y\),把两位对应位做逻辑异或
  • 按位取反:\(!x\),把 \(x\) 的每一位取逻辑非
  • 左移:\(x<<y\),把 \(x\) 的整体向左移动 \(y\) 位,高位抹去,低位补0,(\(x<<y=x*2^{y-1}\))
  • 右移:\(x>>y\),把 \(x\) 的整体向右移动 \(y\) 位,低位抹去,高位补0,(\(x>>y=\cfrac{x}{2^{y-1}}\))(向下取整)

单点修改:

  • 将第 \(i\) 位修改为 \(1\):x |= 1<<(i-1)
  • 保证第 \(i\) 位为 \(1\),修改为 \(0\):x^=1<<(i-1)

注意:\(<<\) 或 \(>>\) 的优先级比 \(-\) 低,则:\(1<<(i-1)=1<<i-1\)

求两个集合的交集,并集和对称差

可以把两个集合看成一个二进制数,则

\(x_i=0\) 表示 \(i\) 不在集合中,反之同理

两个二进制的与,或,异或可以达到需要效果

神器!\(bitset\)

用二进制表示集合时,普通的变量存不下这么大的二进制,我们可以使用 \(bitset\) 创建一个超长二进制

支持:所有位运算操作也支持修改这个串的某一位

时间复杂度为:\(\mathcal{O}(\cfrac{n}{w})\),\(w\) 通常取 \(64\)

基本用法:

  • bitset<N> s 表示创建一个二进制数
  • s.set(i,0/1) 表示在第 \(i\) 位设置成 \(0/1\)
  • s.test(i) 返回第 \(i\) 位的值
  • s.count() 返回 1 的数量
  • s.reset() 把 \(s\) 清空成 0
  • s.flip() 对二进制取反
  • 可以直接进行逻辑运算

注意:声明 \(bitset\) 时,\(N\) 必须是常量(CE不关我事(doge.

\(\mathcal{Practice}\)

见题单

\(\mathcal{Part}\) 7. 单调数据结构

单调栈

单调栈维护的是一个栈,里面的元素单调递增或单调递减

用于找后缀最大值。

定义:当 \(i\) 为后缀最大值时,当且仅当:对于所有的 \(i<j\le|A|\),都有 \(A_i>A_j\)

因为栈内数据单调递减,所以对于一个在栈内的元素 \(A_i\),他后面没有比他大的数(不然就被弹出了

所以留在栈内的都是后缀最大值

时间复杂度为:\(\mathcal{O}(n)\)

单调队列

单调队列维护的是一个队列,只不过里面的数的下标都严格在一个区间里

比你小的人都打不过,你应该出队

被单调队列了

$ Practice$

题比较难,所以我写了几篇题解,自己去看

\(\mathcal{P}art\) 8.倍增

倍增基本概念

我们先来考虑一个引论

  • 任何一个 \(x\in \mathbb{N^*}\),都可以拆成若干个 \(2\) 的正整数次幂之和

\[n=\sum 2^i (i\in\mathbb{N^*}) \]

证明平凡

所以对于每一段区间,我们都可以拆成 \(\log n\) 个区间

倍增快速幂

我们要求 \(x^y \mod p\) 的值

\(x,y\le10^9\)

怎么办呢?

我们考虑使用倍增的思路:

\[x^y=(x^2)^{\frac{y}{2}} \]

这里需要进行奇偶性判断,如果是奇数,则直接乘进答案中即可

时间复杂度为:\(\mathcal{O}(\log x)\)

\(\mathcal{Code}\)

typedef long long ll;
ll qpow(ll d, ll b, ll Mod) {
    ll ans = 1;
    while (b) {
        if (b & 1) ans = ans * d % Mod;
        d >>= 1; b = b * b % Mod;
    }
    return ans;
}

\(\mathcal{P}ractice\)

见题单

ST表

ST表是什么呢?他是专门解决 RMQ 问题的一个简便写法(比线段树好写多了

RNQ 问题

\(\mathcal{O}(n\log n)\) 的预处理,要求支持:

  • 给定 \(l,r\) 要求 \(\max_{l\le i\le r}\{a_i\}\) ,\(\mathcal{O}(1)\) 的时间复杂度

根据我们倍增的知识,我们可以定一个 st 表,如下

记:\(f_{i,j}\) 表示 \([i,i+2^j-1]\) 这段区间的最大值,用人话来讲就是从第 \(i\) 为开始,连续 \(2^j\) 个数

状态转移方程也很好推:

\[f_{i,j}=\max \{f_{i,j-1},f_{i+2^{j-1},j-1}\} \]

怎么记呢?爸爸的爸爸叫爷爷

是不是很好记(doge.

时间复杂度为 \(\mathcal{O}(n\log n)\)

那怎么查询呢?

可以用我们上面证的,一段区间可以拆成若干个2的正整数次幂长度的区间,对这个区间进行二进制分解即可

但这样的时间复杂度为 \(\mathcal{O}(\log n)\)

因为是最大值,所以比较区间可以重叠

这一段区间可以跳 \(\log n\) 次,所以我们就左端点跳 \(\log n\) 次,右端点跳 \(\log n\) 次,这样虽有重叠,也无伤大雅

时间复杂度为 \(\mathcal{O}(1)\)

\(\mathcal{C}ode\)

n = read(), m = read();
for (int i = 1; i <= n; i++) f[0][i] = read();

for (int t = 1; (1 << t) <= n; t++) 
    for (int i = 1; i + (1 << t) - 1 <= n; i++) 
        f[t][i] = max(f[t - 1][i], f[t - 1][i + (1 << t - 1)]);//注意优先级的问题

for (int l, r; m; m --) {
    l = read(), r = read();
    int len = r - l + 1, t = log2(len);
    cout << max(f[t][l], f[t][r - (1 << t) + 1]) << "\n";
}

\(\mathcal{P}ractice\)

见题单

\(\mathcal{Part}\) 10.分治

简述分治算法

简单来说,就是原问题分成两个差不多一样的问题,先解决他们,在返回来解决自己

重要的是:你在做这一层的时候,你不要管下去后他怎么了,不然会晕的,把他默认成已知就行

基本步骤

  • "分":如何把大问题划分为更小的答案
  • "合":如何把小问题合并成更大的答案

归并排序

  • “分“

很显然,当前区间的子任务就是长度一半的区间,即分成两个区间长度都为当前区分的一半

  • "合"

重要部分是 “合”,即现在有两个有序的序列,要把他们合并到一个大的序列里,怎么做呢?

有序表合并,可以用我们刚讲过的双指针思想

即记 \(i\) 为 \(A\) 序列的起点,\(j\) 为序列 \(B\) 的起点

现在比较 \(A_i\) 和 \(B_j\)

  • \(A_i>B_j\),就将 \(B_j\) 加到答案序列里,同时 \(j\) 自增
  • \(A_i<B_j\),就将 \(A_i\) 加到答案序列里,同时 \(i\) 自增

最后如果 \(i\) 或者 \(j\) 没有跑完,就把后面的数直接加进去即可

注意到,答案序列只在原来区间的位置,所以起点和终点就是 \(l,r\)

\(\mathcal{C}ode\)

void solve (int l, int r) {
	if (l == r) return ;
	int mid = l + r >> 1;
	solve(l, mid); solve(mid + 1, r);//分
	int i = l, j = mid + 1, pos = l;
	while (i <= mid && j <= r) {
		if (a[i] <= a[j]) b[pos++] = a[i++];
		else b[pos++] = a[j++];
	}		
	while (i <= mid) b[pos++] = a[i++];
	while (j <= r) b[pos++] = a[j++];
	for (int i = l; i <= r; i++) a[i] = b[i];
	
}

归并排序解决逆序对问题

逆序对问题

现求 \(A\) 序列中满足 \((i,j)\),\(i<j\) 且 \(A_i>A_j\) 的 \((i,j)\) 的数量

对于 \([l,r]\) 的区间,我们分治的去考虑问题,逆序对只有可能出在三种情况

  • 全部出现在左区间
  • 全部出现在右区间
  • 横跨在两个区间之间

前两个操作可以在递归中完成

我们主要看第三个部分

令 \(i\) 为 \(A\) 序列的指针, \(j\) 为 \(B\) 序列的指针

还是分类讨论

  • \(A_i>B_j\),说明 \(A_{i…n}\) 的数都比 \(B_j\) 大,把后面的答案统计一下就行,再将 \(B_j\) 加到答案序列里,同时 \(j\) 自增
  • \(A_i<B_j\),就将 \(A_i\) 加到答案序列里,同时 \(i\) 自增

最后如果 \(i\) 或者 \(j\) 没有跑完,就把后面的数直接加进去即可

\(\mathcal{C}ode\)

void solve (int l, int r) {
	if (l == r) return ;
	int mid = l + r >> 1;
	solve(l, mid); solve(mid + 1, r);
	int i = l, j = mid + 1, pos = l;
	while (i <= mid && j <= r) {
		if (a[i] <= a[j]) b[pos++] = a[i++];
		else b[pos++] = a[j++], ans += mid - i + 1;//改动
	}		
	while (i <= mid) b[pos++] = a[i++];
	while (j <= r) b[pos++] = a[j++];
	for (int i = l; i <= r; i++) a[i] = b[i];
	
}

CDQ分治

这东西,有亿点恶心,耗了我两年半进行理解……

一维偏序

排序即可

二维偏序

例子:逆序对

他就是用归并排序解决的一个二维偏序问题

例子:树状数组

现有一个序列,要支持两种操作

  • 给定 \(x,k\) 要求 \(A_x\) 加上 \(k\) (单点修改
  • 给定 \(x,y\),要求 \(\sum_{x\le i\le y} A_i\)(区间查询

分析:

如果你要用数据结构,我也拦不到你

我们考虑二维偏序

记一个多元祖 \((t,x,w,op)\),\(t\) 为操作编号,\(x\) 为操作位置,\(w\) 为修改值,\(op\) 为对答案的贡献倍数(1/-1)

对于一个查询操作 \(i\),其结果就是所有满足 \(t_j<t_i\) 且 \(p_j<p_i\) 的查询 \(w\) 之和,类似顺序对问题

因为前面的数天生满足 \(t_j<t_i\),我们只需要找到 \(p_j<p_i\) 的 \(w_j\) 之和

考虑分治

设 \(solve(l,r)\) 表示 \([l,r]\) 操作的贡献,\(solve(l,mid)\) 和 \(solve(mid+1,r)\) 时已知,因为左区间和右区间都已经满足两个条件

所以双指针即可

这里给上代码:

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 2e6 + 7;

struct Node{
	int type, x, v, id;
	bool operator < (const Node other) const {
		return x == other.x ? type < other.type : x < other.x;//注意,先修改后查询
	}//现区间没影响,合起来之后有影响
}A[MAXN], B[MAXN];

int n, m, cnt, Ans[MAXN], arr;

void CDQ(int l, int r) {
	if (l == r) return ;
	int mid = l + r >>1;
	CDQ(l, mid), CDQ(mid + 1, r);
	int i = l, j = mid + 1, sum = 0, tot = l;
	while (i <= mid && j <= r) {
		if (A[i] < A[j]) {
			if (A[i].type == 1) sum += A[i].v;//双指针:让左节点一直跳,跳到不符合条件即可
			B[tot ++] = A[i ++];
		} else {
			if (A[j].type == 2) Ans[A[j].id] -= sum;//跳完之后减上贡献
			else if (A[j].type == 3) Ans[A[j].id] += sum;//或加
			B[tot ++] = A[j ++];
		}//记得是累加哦
	}
	while (i <= mid) B[tot ++] = A[i ++];
	while (j <= r) {
		if (A[j].type == 2) Ans[A[j].id] -= sum;//累加即可
		if (A[j].type == 3) Ans[A[j].id] += sum;
		B[tot ++] = A[j ++];
	}
	for (int i = l; i <= r; i ++) A[i] = B[i];
}

int main () {
	cin >> n >> m;
	for (int i = 1, x; i <= n; i ++) cin >> x, A[++ cnt].type = 1, A[cnt].x = i, A[cnt].v = x;
	for (int i = 1; i <= m; i ++) {
		 int op, x, k;
		 cin >> op >> x >> k;
		 if (op == 1) {
		 	A[++ cnt].type = 1, A[cnt].x = x, A[cnt].v = k;
		 } else {
		 	A[++ cnt].type = 2, A[cnt].x = x - 1, A[cnt].id = ++ arr;//arr需相等
		 	A[++ cnt].type = 3, A[cnt].x = k, A[cnt].id = arr;
		 }
	}
	CDQ(1, cnt);
	for (int i = 1; i <= arr; i ++) cout << Ans[i] << '\n';
	return 0;
}

三维偏序

题目:

这是一道三维偏序模板题,可以使用 bitset,CDQ 分治,KD-Tree 等方式解决。

有 $ n $ 个元素,第 $ i $ 个元素有 $ a_i,b_i,c_i $ 三个属性,设 $ f(i) $ 表示满足 $ a_j \leq a_i $ 且 $ b_j \leq b_i $ 且 $ c_j \leq c_i $ 且 $ j \ne i $ 的 \(j\) 的数量。

对于 $ d \in [0, n) $,求 $ f(i) = d $ 的数量。

$ 1 \leq n \leq 10^5$,$1 \leq a_i, b_i, c_i \le k \leq 2 \times 10^5 $。

解决:

第一步,先按第一关键字排序(sort)

第二步:用cdq对第二关键字排序

第三步:用树状数组维护第三关键字(类似用树状数组维护逆序对)

具体的和一些细节:

我们先用 sort 对第一关键字进行排序,保证每个符合条件的 \(j\) 都在 \(i\) 的右边

我们直接上cdq对第二关键字排序

最后用树状数组就行

如果有重复的情况怎么办?

考虑到有可能有 \(j\) 在 \(i\) 的右边,导致没算全,但是最右边的 \(i\) 是绝对正确的,所以我们可以把序列排序,然后把连着的相同的全部赋值成最后一个数即可

注意细节

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 1e5 + 7;
const int MAXM = 2e5 + 7;

struct Node{
	int a, b, c, id;
	bool operator < (const Node other) const {
		if (a != other.a) return a < other.a;
		if (b != other.b) return b < other.b;
		if (c != other.c) return c < other.c;
		return id < other.id;
	}
}A[MAXN], B[MAXN];

int n, k;

int D[MAXM], K[MAXN], H[MAXN];

namespace BIT {
	#define lb(i)(i & -i)
	void modify(int x, int v) {
		for (; x <= k; x += lb(x)) D[x] += v;
	}
	void query(int x, int &r) {
		for (; x; x -= lb(x)) r += D[x];
	}
}

void CDQ(int l, int r) {
	if (l == r) return ;
	int mid = l + r >> 1;
	CDQ(l, mid); CDQ(mid + 1, r);
	int i = mid + 1, j = l, tot = l;
	while (i <= r && j <= mid) {
		if (A[i].b >= A[j].b) //符合,考虑相等情况 
			BIT :: modify(A[j].c, 1), B[tot ++] = A[j ++];
		else
			BIT :: query(A[i].c, K[A[i].id]), B[tot ++] = A[i ++];//k数组没有顺序问题,只是一个容器 
	}
	while (j <= mid) BIT ::modify(A[j].c, 1), B[tot ++] = A[j ++];//注意到,左半部分只会加1,所以清零时免得清成负数 
	while (i <= r) BIT :: query(A[i].c, K[A[i].id]), B[tot ++] = A[i ++];//剩下的都是可以 
	for (int i = l; i <= mid; i ++) BIT :: modify(A[i].c, -1);
	for (int i = l; i <= r; i ++) A[i] = B[i];
}

int main () {
	ios :: sync_with_stdio(false); cin.tie(NULL);
	cin >> n >> k;
	for (int i = 1; i <= n; i ++) cin >> A[i].a >> A[i].b >> A[i].c, A[i].id = i;
	sort(A + 1, A + 1 + n);
	CDQ(1, n);
	sort(A + 1, A + 1 + n);
	for (int i = n; i >= 1; i --) {
		if (A[i].a == A[i + 1].a && A[i].c == A[i + 1].c && A[i].b == A[i + 1].b) //完全一样 
			K[A[i].id] = K[A[i + 1].id];
		//排序之后,相等的数肯定是连在一起的 
		//对一维排序的原因是让i的符合条件的j都在i的左边,相等的情况j在i的右边我们统计不到,但是最后的那个数一定正确 
		H[K[A[i].id]] ++;
	}
	for (int i = 0; i < n; i ++) cout << H[i] << '\n';
	return 0;
}

更高维的偏序

以四维偏序为例,我们按第一关键字排序

给第二关键字cdq时,我们发现第一维被打乱了

但是我们只需要的知道他在左边还是右边,所以我们就可以记录第一维的 \(LEFT\) 和 \(RIGHT\)。

再对第三位进行cdq,第四维用树状数组维护即可

偏序的时间复杂度

用主定理可以求解

逆序对是 \(\mathcal{O}(N\log n)\)

三维偏序是 \(\mathcal{O}(N\log ^2n)\)

\(k\) 维偏序时 \(\mathcal{O}(N\log^{k-1}n)\)

\(\mathcal{Part}\) 11. 尾声

算法思想多加练习即可

标签:cnt,le,思想,int,mid,算法,数组,mathcal
From: https://www.cnblogs.com/Phrvth/p/17300594.html

相关文章

  • 人工智能概率算法-模拟神经元结构预测价格
    最近研究人工智能概率算法,想通过统计学的方式预测未来比较好的例子就是股票,历史数据很丰富输入端:4个参数(开盘价、最高价、最低价、收盘价)输出端:4个参数第二天(开盘价、最高价、最低价、收盘价)把价格从-10到+10,每次迭代0.1,分类成200个特征刚开始神经元的输入端不敏感,细胞核不......
  • 算法基础
    语言基础取地址符我们可以用&读取变量的地址。特别的,对于数组,使用"数组名+元素"可以获得该变量的地址。例如\(f+1\)就是\(f\)数组第\(1\)个元素的地址。在C/C++中,指针变量的类型为类型名后加上一个*,例如int类型的指针为int*。要想访问指针变量地址所对应的......
  • CSCI561 算法解析
    CSCI561CSCI561FirstOrderLogicResolutioGuidelinesThisisaprogrammingassignment.Youwillbeprovidedwithsampleinputsandoutputs(seebelow).Pleaseunderstandthatthegoalofthesamplesistocheckthatyoucancorrectlyparsetheproblemdefi......
  • 数组的算法
    数值型数组特征值统计这里特征值涉及到:平均值,最大值,最小值,总和等求最大值:将数组第一个元素假设为最大值intmax=arr[0];再然后用写一个判断语句如果数组第一个元素小于当前比较的元素就把当前比较的元素赋值给maxif(max<arr[i]){max=arr[i]}求最小值:定义一个变量这个数大......
  • 【算法题】831. 隐藏个人信息
    题目:给你一条个人信息字符串s,可能表示一个邮箱地址,也可能表示一串电话号码。返回按如下规则隐藏个人信息后的结果:电子邮件地址:一个电子邮件地址由以下部分组成:一个名字,由大小写英文字母组成,后面跟着一个‘@’字符,后面跟着一个域名,由大小写英文字母和一个位于中间的......
  • Lasso回归_ElasticNet回归_PolynomialFeatures算法介绍---人工智能工作笔记0032
    然后我们再来看这个ridge回归,可以看到这里的这个岭回归,可以看到他的损失函数,其实就是添加了一个使用L2的正则化的,惩罚项对吧,目的是为了增强,损失函数的泛化能力,这里的alpha,实际上作用是为了,调整,这个损失函数的,正确率多一点还是泛化能力强一点. 可以看到他的使用函数的方......
  • java-信息安全(二十)国密算法 SM1,SM2,SM3,SM4
    一、概述国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。目前主要使用公开的SM2、SM3、SM4三类算法,分别是非对称算法、哈希算法和对称算法。SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进......
  • 快速幂算法
    快速幂算法设计一个算法计算\(x^n\)的值。根据定义最常见也最能瞬间想到的是如下的算法://递归写法publicintpow1(intx,intn){if(n==0)return1;if(n==1)returnx;returnx*pow1(x,n-1);}//循环写法publicintpow2(intx,intn){inty......
  • 算法学习之冒泡排序【C语言】
    冒泡排序排序规则冒泡排序的规则是相邻的两个数字依次比较,如果前面的数字比后面的数字大,则交换它们的位置,否则保持不变,直到遍历完所有的数字。这个过程会不断地进行,直到所有的数字都按照从小到大的顺序排列好。双层循环在冒泡排序的算法中,需要使用两层循环来实现排序功能。for(int......
  • 算法-递归三(树形结构)
    publicclassSolution{publicIList<IList<int>>Permute(int[]nums){varrtItem=newList<int>();varvisited=newDictionary<int,bool>();IList<IList<int>>rt=newList<IList<int&......