2021/4/17
-
多个选项中选多个的题目,计算合法方案数。这种题目可以利用全排列来写
- 六个里边选三个
int arr[]={0,0,0,1,1,1,};
- 六个里边选三个
-
消除尾一:
x=x&(x+1)
- 经典的二进制题目!
- 经典的二进制题目!
-
10个数的全排列,时间复杂度为
10!
-
最大公共子串
和最大公共子序列
在二维动态规划时,是不一样的- 前者的状态转移是
if(s1[i-1]==s2[i-1]) dp[i][j]=dp[i-1][j-1]+1;
- 后者需要分三种情况进行讨论。
- 前者的状态转移是
-
背包问题的精髓在于:在递归或动态规划时,假设背包中已经装了xx的情况下,看剩下的容量怎么装。
2021/4/16
- 若a、b、c三者和的余数为K,a的余数为i,b的余数为j
- 则c的余数为
(K-i+K-j)%K
- 即
(i以外需要凑的余数+j以外需要凑的余数)对K取余
- 则c的余数为
- 多重循环时间复杂度优化
- 减少层数
- 减少范围
- 能否二分
- 空间换时间
- 倍数问题大多跟余数有关!
- 多个数的和是某个数的倍数,意味着,各个数的余数的和等于目标数;
- 动手编程之前,把问题想清楚,思路理清楚,每一步要做什么搞清楚,先注释,再编程。
- 对于多维数组的问题,可以转换成一维进行处理
- 如三维转一维
int convert(int i,int j,int k){
int a=i-1,b=j-1,c=k-1;
return a*row*column+b*column+c;
}
2021/4/14
- python的数不会溢出,因此比赛填空题有些可以用python来解决
- 取模与取余的区别在于
- 取余是向0方向舍入(fix函数)
- 取模是向负无穷方向舍入(floor函数)
- 如-7 mod 4= 1 (到-8不足1), -7 %4 = -3 (到-4少了-3)
2021/4/13
- 快速幂的精髓在于,将底数不断膨胀,幂不断缩小
递归写法
long long fastPow(long long base,int pow){
if(pow==0)
return 1;
if(pow==1)
return base;
if(pow%2==1){
return base*fastPow(base*base,pow/2);
}
return fastPow(base*base,pow/2);
}
非递归位运算写法
long long fastPow(long long base,int pow){
long long res=1;
while(pow){
//处理奇数
if(pow&1){
res*=base;
}
base*=base;
pow>>=1;
}
return res;
}
2021/4/12
- 凡是在一组值中,选出一部分,在一定规则下组合成一个目标值,就可以看作是背包问题
- 背包问题的精髓在于将目标值分解成
0~n
来逐个解决,每次选取数组中一个值时,对背包剩下的容量进行调整(一般是减少,也可能增加),装入背包后,剩余容量用已有的dp[j]来解决是背包问题能动态规划的核心原因。
- 背包问题的精髓在于将目标值分解成
2021/4/11
- 一些动态规划的问题,考虑将dp[i]是为,以i为结尾的xxx;
- 使用dp数组,首先要搞明白dp[i]代表什么含义,可以是题中的要求求解的量,也可以是中间结果。并思考,dp[i]和dp[i-1](或者dp[i]和dp[0],dp[1]…dp[i-1])有什么关系。
- 对于动态规划(或者其他问题)中的环形问题,可以采取分类的方法
- 选第一个且不选最后一个,求出最优解
- 选最后一个且不选第一个,求出最优解
- 有一部分动态规划问题的精髓在于,对每一种情况进行试探,从而找出最优解。(不是返回dp[len-1]就行了,而是max(dp[i]) )
- 比如leetcode121题股票交易,这是一道easy题,但是我想了好久没做出来,为什么?因为对动态规划题目定势思维了,认为dp[i]存储的就是最终结果,其实它也可以是中间结果,或者是需要进一步处理的信息。
- 对这种题目,我们可以尝试在每一天卖出股票,从而找到最优的方案。
2021/4/10
-
动态问题一定要搞清楚对谁进行0~n的划分(如果coins问题是对金额进行划分),也有可能是多个变量进行划分(二维、三维dp,但也可压缩。
-
01背包问题为什么要倒序遍历(压缩的)dp数组
- 比如在leetcode474中,这样一个临时的二维dp
- 假如从左上角往右下角遍历,每次利用i,j位置之前的数据修改dp[i][i],就会造成上一轮的临时数据已经被修改,后边的数据无法利用上一轮的临时数据了。而从右下角向左上角遍历,就不会出现这个问题。
- 比如在leetcode474中,这样一个临时的二维dp
-
01背包问题,动态规划的思路是:物品一个一个尝试,容量一点一点尝试,每个物品分类讨论的标准是:选与不选
2021/4/9
- 对于动态规划中的初始条件问题(尤其是二维dp),可以额外写一个函数来处理,如下这个LCS最长公共子序列问题,使用
safeReturn函数来处理第一行第一列的特殊情况
:
class Solution
{
public:
int safeReturn(vector<vector<int> >&dp,int x,int y)
{
if(x<0 || y<0)
return 0;
return dp[x][y];
}
int longestCommonSubsequence(string text1, string text2)
{
int len1=text1.size(),len2=text2.size();
//dp[i][j]表示,text1前i个字符和text2前j个字符的LCS数
vector<vector<int> >dp(len1,vector<int>(len2,0));
for(int i=0; i<len1; i++)
for(int j=0; j<len2; j++)
{
if(text1[i]==text2[j])
{
dp[i][j]+=1;
dp[i][j]+=safeReturn(dp,i-1,j-1);
}
else
{
dp[i][j]=max(safeReturn(dp,i-1,j),safeReturn(dp,i,j-1));
}
}
return dp[len1-1][len2-1];
}
};
- 对于动态规划问题,
dp[i]
记录的可以是中间结果,也可以是最终结果。如果记录的是中间结果,最后不能忘了遍历并返回最大值或最小值
。
2021/4/7
- 排列与组合
- 对于排列题,使用
next_permutation
或者自定义DFS+回溯
的递归函数实现; - 对于组合题,使用递归对每个位置采取
选中
和不选中
两种方法。
- 对于排列题,使用
- 使用DFS+回溯编写不重复的全排列,在for循环的交换中,可以使用set集合来避免重复
- 注意!使用之前,序列必须已经排好序!
- 注意!内部交换时,仅仅让交换过来的数不等于自身,是远远不够的,因为可能多次交换过来同一个数,需要使用set才能完全去重。
void permutation(vector<int>&nums,int k){
//在下一轮进行判断
if(k==nums.size()){
ans.push_back(nums);
return;
}
set<int>record;
for(int i=k;i<nums.size();i++){
int target=nums[i];
if(record.find(target)!=record.end()){
continue;
}
record.insert(target);
swap(nums[i],nums[k]);
permutation(nums,k+1);
swap(nums[i],nums[k]);
}