搜索
BFS:用于解决最短路问题,起始态到终点态的最短路劲,另一个运用是在地图中找连通块,类似洪水填充。因为BFS我实在是太熟悉,所以三言两语带过了。
DFS:其实就是暴力搜索,时间复杂度很高。
记忆化:在DFS加入dp
int dfs(int curi,int x) {
if (curi == 1 && x == m)return 1;
else if (x == m && curi != 1)return 0;
else if (dp[curi][x])return dp[curi][x];
int shun = curi + 1;
if (shun == n + 1)shun = 1;
int ni = curi - 1;
if (ni == 0)ni = n;
int ans= dfs(shun,x+1)+ dfs(ni, x + 1);
dp[curi][x] = ans;
return ans;
}
数学
快速幂:运用了倍增的思想,用于快速求一个数的幂
int Binexp(int a, int n)
{
int ans = 1;
while (n!=0)
{
if (n % 2 == 1)
{
ans *= a;
}
a *= a;
n /= 2;
}
return ans;
}
矩阵:
A*B的条件是:A的列=B的行
方法是A的行*B的列
2*2+4*3 2*3+4*2 2*2+3*3
3*2+2*3 3*3+2*2 3*2+2*3
vector<vector<int>> multiply(vector<vector<int>>& a, vector<vector<int>>& b) {
int n = a.size();
int m = b[0].size();
int k = a[0].size();
vector<vector<int>> ans(n, vector<int>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
for (int c = 0; c < k; c++) {
ans[i][j] += a[i][c] * b[c][j];
}
}
}
return ans;
}
矩阵快速幂:可以加速斐波那契的计算
vector<vector<int>> power(vector<vector<int>>& m, int p) {
int n = m.size();
//一定是正方型
vector<vector<int>> ans(n, vector<int>(n, 0));
for (int i = 0; i < n; i++) {
ans[i][i] = 1;
}//把对角线设置为1 单位矩阵 矩阵n * 单位矩阵=矩阵n
for (; p != 0; p >>= 1) {
if ((p & 1) != 0) {
ans = multiply(ans, m);
}
m = multiply(m, m);
}
return ans;
}
筛法:
void e()
{
int n; cin >> n;
vector<int> prime(n + 1, 1);//假设全是质数
prime[0] = 0; prime[1] = 0;
for (int i = 2; i * i <= n; i++)
{
if (prime[i] == 1)
for (int j = i * i; j <= n; j += i)
{
prime[j] = 0;
}
}
}
bool isprime[N + 1];
int prime[N + 1];
void Eula()
{
memset(isprime, true, sizeof(isprime));
isprime[1] = false;
for (int i = 2; i <= n; ++i)
{
if (isprime[i]) prime[++cnt] = i;
for (int j = 1; j <= cnt && i * prime[j] <= n; ++j)
{
isprime[i * prime[j]] = false;
if (i % prime[j] == 0) break;
}
}
}
gcd和lcm:比较实用
int gcd(int a, int b) {
if (b == 0)return a;
else return gcd(b, a % b);
}
int lcm(int a, int b) {
return a * b / gcd(a, b);
}
同余原理:计算(a+b)%M 或者(a*b)%M可能会在中途爆掉
(a + b) % m = (a % m + b % m) % m;
(a * b) % m = (a % m * b % m) % m;
算术基本定理:
对于一个大于1的正整数n可以分解质因数 每一个P都是N的质因数
void f(int a)
{
vector<int>ans;
for (int i = 2; i * i <= a; i++)
{
if (a % i == 0)
{
ans.push_back(i);
/* cout << i << " ";*/
}
while (a % i == 0)
{
a /= i;
}
}
if (a > 1) {
/* cout << a << " ";*/
ans.push_back(a);
}
}
定理的应用:
1.正因数个数
之前被一道题坑了
给出求正因数个数的代码:注意这里一定是i*i<=x 复杂度是根号N 最后if判断也是很重要的
int fenjie(int x) {
int ans = 1;
for (int i = 2; i*i <= x; i++) {
int cnt = 0;
while (x % i == 0) {
cnt++;
x /= i;
}
ans *= (cnt + 1);
}if (x != 1) {
ans *= 2ll;
}
return ans;
}
2.正因数之和
逆元:
求解逆元:
1.快速幂求解逆元:保证a/b==0 p是质数 b和p互质
int qpow(long long a, int b, int p) {
//b是质数
int ans = 1;
a = (a % p + p) % p;
for (; b; b >>= 1) {
if (b & 1) ans = (a * ans) % p;
a = (a * a) % p;
}
return ans;
//调用qpow(a,p-2,p)
}
简而言之:a的逆元=a的(模-2)次方%M
2.exgcd求逆元:x就是逆元
void exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1, y = 0;
return;
}
exgcd(b, a % b, y, x);
y -= a / b * x;-->逆元
}
//要求gcd(a,b)==1
运用:
(a/b)%M=(a%M)*(b的逆元)%M
同余方程:
x其实就是逆元 不过只能拿exgcd求解
//只能拿exgcd求解
void exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1, y = 0;
return;
}
exgcd(b, a % b, y, x);
y -= a / b * x;
}
int a, b; cin >> a >> b;
int x = 0, y = 0;
exgcd(a, b, x, y);
x = (x % b + b) % b;//负数也有可能是同余方程的解 这一步是除负数的影响
数据结构STL
1:map
map底层是红黑树 根据key从小到大排序 map有[ ]
记住常用的即可:
size(),empty(),clear()... 重试 错误原因
find(key)返回迭代器
map<int, int>mp;
mp[1] = 2;
auto it = mp.find(4);
if(it!=mp.end())
cout << it->second;
auto it = mp.find(1);
if (it != mp.end())
cout << it->second;
count(key)只返回0或者1
map<int, int>mp;
mp[1] = 2;
mp.count(1); //return 1
mp.count(0); //return 0
2.set
set底层是红黑树从小到大排序 自动去重 没有[ ]
3.queue
先进先出
4.stack 重试 错误原因
先进后出
5.priority_queue
大根堆:
小根堆:
6.deque
直接用双向链表模拟
比较器:重要!
动态规划入门:
线性DP:
1:子数组最大累加和
比较简单不想多说
int maxSubArray(vector<int>& nums) {
dp[0]=nums[0];
for(int i=1;i<nums.size();i++)
{
dp[i]=max(dp[i-1]+nums[i],nums[i]);
}
return *max_element(dp,dp+nums.size());
}
2.最长上升/不降/下降子序列
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
int ans = 0;
for (int i = 0; i < n; i++)
{
dp[i] = 1;
for (int j = 0; j < i; j++)
{
if (nums[i] > nums[j])//>=
{
dp[i] = max(dp[i], dp[j] + 1);
}
//if (nums[j] > nums[i])最长下降子序列
//{
// dp[i] = max(dp[i], dp[j] + 1);
//}
}ans = max(ans, dp[i]);
}
return ans;
}
简单的说 对于任意一个位置 去找前面位置 如果前面位置有数字小于当前位置 更新一波答案
递推下去求解
优化:
#include<iostream>
#include<vector>
#include<algorithm>x
using namespace std;
#define maxn 2510
int endss[maxn];
//endss数组一定是单调递增的
int bsl1(vector<int>& nums, int len, int target)
{
//在endss数组中找到大于等于target的最左位置
//比如
//3 4 5 6 7 target=2
//0 1 2 3 4
//那么最左位置因该是0位置
//如果找不到
//3 4 5 6 7 target=8
//返回-1
int l = 0, r = len - 1, m, ans = -1;
while (l <= r) {
m = (l + r) / 2;
if (endss[m] >= target) {
ans = m;
r = m - 1;
}
else {
l = m + 1;
}
}
return ans;
}
int lengthOfLIS(vector<int>& nums)
{
int len = 0;
for (int i = 0, find; i < nums.size(); i++)
{
find = bsl1(nums, len, nums[i]);
if (find == -1)endss[len++] = nums[i];//没找到 加到后面 比如1 2 3 4 tar=5 ,,1 2 3 4 5
else endss[find] = nums[i];//否则修改 比如3 4 5 6 7 target=2,,2 4 5 6 7
}
return len;
}
最大子段和:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
#define INFll 0x3f3f3f3f3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn = 1e6 + 50;
int n, m;
int a[maxn];
int dp[maxn];
int Max[maxn];
//二维数组dp, dp[ i ][ j ],表示前 j 项所构成 i 子段的最大和,且必须包含着第j项,即以第j项结尾
//然后是一个递推过程。
int Solve()
{
/**
二维数组中,dp[i][j]的求解依赖 dp[i][j-1] 以及 max(dp[i-1][j-1],dp[i-1][j-2],dp[i-1][j-3]...)
因为只需要当前行和上一行的最大值
所以,我定义 Max[j] 表示前一行中[i-1,j]的dp最大值
*/
mem(Max, 0);
for (int i = 1; i <= m; i++)
{
dp[i - 1] = -INFll;///i-1个数不能划分成i段,所以将dp[i-1]赋值为-INFll
for (int j = i; j <= n - m + i; ++j)
dp[j] = max(dp[j - 1], Max[j - 1]) + a[j];
Max[i - 1] = -INFll;///i-1个数不能划分成i段,所以将Max[i-1]赋值为-INFll
for (int j = i; j <= n - m + i; ++j)
Max[j] = max(Max[j - 1], dp[j]);
}
return *max_element(dp + m, dp + n + 1);
}
signed main()
{
m = 2;
while (cin >> n)
{
if (n == 0)break;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
cout << Solve() << endl;
}
return 0;
}
背包问题:
之前写过了