ACM寒假训练成果

搜索

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;
}

背包问题:

之前写过了

讲一下01背包,完全背包,多重背包,分组背包-CSDN博客

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值