1:枚举
按照题目要求进行遍历即可,要注意数据范围。
2:进位制
一:10进制转X进制,除以X,把余数保存起来翻转即X进制下对应的数。 二:X进制转10进制,第一位数乘以X的n-1次方,以此递推,求和即 10进制下对应的数。 三:X进制转Y进制,还没学,只能X转10进制,再把10进制转Y进制,只能骗分。
3:双指针算法
能把O(n^2)压缩为O(nlogn)。用i对一个单调递增的序列遍历,用j从i开始遍历,使得该序列所有情况都能计算。
**4:二分 **
只能靠模板。
int l = 0 ,r = n;
while(l<r)
{
int mid = (l+r)/2;
if(check())r=mid;
else l = mid+1;
}
//第二种 这种情况二分的结果偏小
while(l<r)
{
int mid = (l+r+1)/2;
if(check())l=mid;
else l = mid-1;
}
cout<<l+1<<endl;
5:前缀和
一维前缀和和二维前缀和,也不是遇见矩形一定要二维前缀和,可以对列进行前缀和。
6:背包问题
有限空间下的价值最大值。
01背包
for(int i=1;i<=n;i++)
{
for(int j=0;j<=V;j++)
{
if(v[i]<=j)//放的下第i件物品
{
dp[i][j]=max(dp[i-1][j] , dp[i-1][j-v[i]] + w[i]);
}
else //放不下第i件物品
dp[i][j] = dp[i-1][j];//放不下的话 前i件物品在容量j条件下的最大价值 就等于 前i-1件物品最大价值
}
}
完全背包
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(j>=w[i])
dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+v[i]);//01背包问题后面是dp[i]即用更新过的数据继续向后更新
//所以一个物品可以被无限更新(添加)
//01背包 dp[i][j] = max(dp[i-1][j] , dp[i-1][j-w[i]]+v[i]);
else
dp[i][j] = dp[i-1][j];
}
}
多重背包
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--)
{
for(int k=1;k<=s[i] && k*v[i]<=j;k++)//这里加了循环来判断加入k件物品是否会是dp的值更大
{
dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);//相当于把k件物品捆绑 不过和01背包没有什么差别
}
}
}
多重背包的二进制优化
//二进制优化 1 2 4 8 16 ........
//二进制优化nb的在于 能表示出来所有情况 就比如7=1+2+4
/*
题目的意思是某物品最多有s件,我们需要从所有的物品中选择若干件,
使这个背包的价值最大。题目并没有说某物品一定需要选多少件出来,也没有说一共要选多少件出来。
只是选择若干件,至于选几件,无所谓,但要保证价值最大。按照优化的策略某物品有s件,
我们给其打包分成了好几个大的物品。
第一个大物品是包含原来该物品的1件,第二个大物品是包含原来该物品的2件,
第三个大物品是包含原来该物品的4件,第四个大物品是包含原来该物品的8件,.....依次类推。
此时我们就把所有的物品都重新进行了一个分类。
原先每个物品最多s件,我们就把这个件数条件给消去了。
取而代之的是,按照一定原先件数组合出来的新若干大物品。
我们又已知按照我们划分成大物品进行搭配组合,一定能转化为原先的若干件小物品出来。
并且选择某物品的最多件数,是不会超过原先该物品的s件。
所以就转化为从下面这些若干件大物品中,选择能使背包容积最大大的情况下,价值最高。
这个就是一个01问题。
*/
int t = 1;
while(t<=s)
{
k++;
v[k] = x * t;
w[k] = y * t;
s-=t;
t *= 2;
}
if(s>0)
{
k++;
v[k] = x * s;
w[k] = y * s;
}
}
for(int i=1;i<=k;i++)
{
for(int j=m;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
分组背包
//k循环在j前面是01背包
//k循环在j后面是分组背包
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j--)
{
for(int k=1;k<=s[i];k++ )//k的循环 和 j的循环 不能换位置 否则意义就变了
// 现在表示的是在j的空间下 第i组元素的最大取值
// 先进行k循环 和01背包没有区别 体现不出分组的概念
if(j>=v[i][k])
dp[j] = max(dp[j],dp[j-v[i][k]]+w[i][k]);
}
}
7:BFS,DfS
一:记忆化搜索+剪枝 DfS 详见 链接
二:DFS模板
int dfs(int u)
{
st[u] = true; // st[u] 表示点u已经被遍历过
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j]) dfs(j);
}
}
三:BfS模板
queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);
while (q.size())
{
int t = q.front();
q.pop();
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j])
{
st[j] = true; // 表示点j已经被遍历过
q.push(j);
}
}
}
8:异或
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
9:差分
和前缀和相似,有一维差分和二维差分,看适用条件。
10:并查集
模板
int p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
11:KMP
模板
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == m)
{
j = ne[j];
// 匹配成功后的逻辑
}
}
12:字符串哈希
思想是吧字符串转化为一个数,怎么才能确保数不一样呢,那就是采用进制。
核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果
typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h[i] = h[i - 1] * P + str[i];
p[i] = p[i - 1] * P;
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
13:最大公约数 辗转相除法。
假如需要求 1997 和 615 两个正整数的最大公约数,用欧几里得算法,是这样进行的:
1997 ÷ 615 = 3 (余 152)
615 ÷ 152 = 4(余7)
152 ÷ 7 = 21(余5)
7 ÷ 5 = 1 (余2)
5 ÷ 2 = 2 (余1)
2 ÷ 1 = 2 (余0)
至此,最大公约数为1