数列DP进阶

news2024/11/27 21:53:32

目录

一,斜率优化

1,斜率优化原理

2,凸包和斜率计算

3,实战

黑暗爆炸 - 4709 柠檬

二,else

力扣 644. 子数组最大平均数 II(最大子段和+二分)

​力扣 646. 最长数对链

 力扣 1235. 规划兼职工作

 力扣 1340. 跳跃游戏 V

 力扣 1696. 跳跃游戏 VI(不能用备忘录)

 力扣 1335. 工作计划的最低难度


本文列举的都是不满足数列DP基础中的通项公式的问题。

一,斜率优化

有一类单数列DP问题,用公式形如dp(i) = max{dp(j)+f(i,j) | j<i},相比简单的单数列DP问题,这个递推式的时间复杂度显然更高。

如果f函数满足一定的条件,则可以使用斜率优化加快速度。

1,斜率优化原理

如果,递推公式可以表示成dp(i) = max{dp(j) + f(i,j) | j<i且...}

其中f(i,j)可以表示成g1(i)g2(j)+g3(i)+g4(j),其中g1,g2,g3,g4是4个函数,且g2具有单调性

那么,递推公式可以化成dp(i)=g3(i)+max{dp(j)+g4(j) + g1(i)g2(j)}

于是问题转化为,在一个点集中,求y-kx的最大值(或最小值),其中x是g2(j),y是dp(j)+g4(j),k是-g1(i)

而快速求解这个最值的方法就是凸包+二分

2,凸包、y-kx最值计算

凸包

3,实战

黑暗爆炸 - 4709 柠檬

Description

Flute 很喜欢柠檬。它准备了一串用树枝串起来的贝壳,打算用一种魔法把贝壳变成柠檬。贝壳一共有 N (1 ≤ N ≤ 100,000) 只,按顺序串在树枝上。为了方便,我们从左到右给贝壳编号 1..N。每只贝壳的大小不一定相同,贝壳 i 的大小为 si(1 ≤ si ≤10,000)。变柠檬的魔法要求,Flute 每次从树枝一端取下一小段连续的贝壳,并选择一种贝壳的大小 s0。如果 这一小段贝壳中 大小为 s0 的贝壳有 t 只,那么魔法可以把这一小段贝壳变成 s0t^2 只柠檬。Flute 可以取任意多次贝壳,直到树枝上的贝壳被全部取完。各个小段中,Flute 选择的贝壳大小 s0 可以不同。而最终 Flute 得到的柠檬数,就是所有小段柠檬数的总和。Flute 想知道,它最多能用这一串贝壳变出多少柠檬。请你帮忙解决这个问题。

Input

第 1 行:一个整数,表示 N。

第 2 .. N + 1 行:每行一个整数,第 i + 1 行表示 si。

Output

仅一个整数,表示 Flute 最多能得到的柠檬数。

Sample

InputcopyOutputcopy
5
2
2
5
2
3
21
//Flute 先从左端取下 4 只贝壳,它们的大小为 2, 2, 5, 2。选择 s0 = 2,那么这一段
里有 3 只大小为 s0 的贝壳,通过魔法可以得到 2×3^2 = 18 只柠檬。再从右端取下最后一
只贝壳,通过魔法可以得到 1×3^1 = 3 只柠檬。总共可以得到 18 + 3 = 21 只柠檬。没有
比这更优的方案了。

思路:

dp[i]表示前i个贝壳可以变成多少柠檬,1<=i<=n,dp[n]就是本题答案。

arr[i]表示输入的数据,1<=i<=n

num[i]表示arr的前i个数中,等于arr[i]的有多少个(至少1个)

递推式:dp[i] = max{dp[j-1] + arr[i] * (num[i]-num[j]+1)^2 | 1<=j<=i, arr[j]==arr[i]}

dp[0]=0

斜率优化:

dp[i]=arr[i]* (num[i]+1)^2 + max{dp[j-1] + arr[j] * num[j] * (num[j]-2num[i]-2) | 1<=j<=i, arr[j]==arr[i]}

即dp[i]=arr[i]* (num[i]+1)^2 + max{dp[j-1] + arr[j] * num[j] * (num[j]-2) -2num[i] * arr[j] * num[j] | 1<=j<=i, arr[j]==arr[i]}

这就是求y-kx的最大值,y=dp[j-1] + arr[j] * num[j] * (num[j]-2), k=2num[i], x=arr[j] * num[j]

虽然arr[j] * num[j]并不是关于j的单调函数,但是在{j|arr[j]==arr[i]}这个生成空间中,是单调的。

int main()
{
	vector<int>arr, num;
	vector<long long>dp;
	map<int, int>sumnum;
	int n;
	cin >> n;
	arr.resize(n + 1), num.resize(n + 1), dp.resize(n + 1);
	dp[0] = 0;
	for (int i = 1; i <= n; i++) {
		cin >> arr[i];
		sumnum[arr[i]]++;
		num[i] = sumnum[arr[i]];
	}
	map<int, Andrew>m;
	for (int i = 1; i <= n; i++) {
		m[arr[i]].push(Point{ arr[i] * 1.0 * num[i]  ,
			dp[i - 1] + arr[i] * 1.0 * num[i] * (num[i] - 2) });
		double k = 2.0 * num[i];
		Point p = MinMaxWithSlope(m[arr[i]].up, k, "max");
		dp[i] = arr[i] * 1.0 * (num[i] + 1) * (num[i] + 1) + p.y - k * p.x;
	}
	cout << dp[n];
	return 0;
}

二,else

力扣 644. 子数组最大平均数 II(最大子段和+二分)

给你一个包含 n 个整数的数组 nums ,和一个整数 k 。

请你找出 长度大于等于 k 且含最大平均值的连续子数组。并输出这个最大平均值。任何计算误差小于 10-5 的结果都将被视为正确答案。

示例 1:

输入:nums = [1,12,-5,-6,50,3], k = 4
输出:12.75000
解释:
- 当长度为 4 的时候,连续子数组平均值分别为 [0.5, 12.75, 10.5] ,其中最大平均值是 12.75 。
- 当长度为 5 的时候,连续子数组平均值分别为 [10.4, 10.8] ,其中最大平均值是 10.8 。
- 当长度为 6 的时候,连续子数组平均值分别为 [9.16667] ,其中最大平均值是 9.16667 。
当取长度为 4 的子数组(即,子数组 [12, -5, -6, 50])的时候,可以得到最大的连续子数组平均值 12.75 ,所以返回 12.75 。
根据题目要求,无需考虑长度小于 4 的子数组。
示例 2:

输入:nums = [5], k = 1
输出:5.00000
 

提示:

n == nums.length
1 <= k <= n <= 104
-104 <= nums[i] <= 104

思路:二分+DP

//获取列表中最大值
template<typename T>
T GetMax(const vector<T> &v)
{
	T ans = v[0];
	for (int i = 1; i < v.size(); i++)ans = max(ans, v[i]);
	return ans;
}
//获取列表中最小值
template<typename T>
T GetMin(const vector<T> &v)
{
	T ans = v[0];
	for (int i = 1; i < v.size(); i++)ans = min(ans, v[i]);
	return ans;
}
//求以每个元素开头的最大子段和
template<typename T>
vector<T> maxSubArrayFromEver(const vector<T> &v)
{
	vector<T>ans = v;
	for (int i = v.size() - 2; i >= 0; i--) {
		if (ans[i + 1] >= 0)ans[i] += ans[i + 1];
	}
	return ans;
}
class Solution {
public:
	bool ok(const vector<int> &anums, int k, double avg)
	{
		vector<double>nums(anums.size());
		for (int i = 0; i < nums.size(); i++)nums[i] = anums[i] - avg;
		auto msub = maxSubArrayFromEver(nums);
		double s = 0;
		for (int i = 0; i < k; i++)s += nums[i];
		for (int i = k - 1; i < nums.size(); i++) {
			if (i >= k)s += nums[i] - nums[i - k];
			if (s >= 0)return true;
			if (i < nums.size() - 1 && s + msub[i + 1] >= 0)return true;
		}
		return false;
	}
	double findMaxAverage(vector<int>& nums, int k) {
		double amin = GetMin(nums);
		double amax = GetMax(nums);
		while (amax - amin >= 0.00001) {
			double mid = (amax + amin) / 2;
			if (ok(nums, k, mid))amin = mid;
			else amax = mid;
		}
		return amin;
	}
};

​力扣 646. 最长数对链

题目:

给出 n 个数对。 在每一个数对中,第一个数字总是比第二个数字小。

现在,我们定义一种跟随关系,当且仅当 b < c 时,数对(c, d) 才可以跟在 (a, b) 后面。我们用这种形式来构造一个数对链。

给定一个对数集合,找出能够形成的最长数对链的长度。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。

示例 :

输入: [[1,2], [2,3], [3,4]]
输出: 2
解释: 最长的数对链是 [1,2] -> [3,4]
注意:

给出数对的个数在 [1, 1000] 范围内。

分析:

动态规划。

因为数据量只有1000,所以直接写个简单的O(n*n)的时间的算法即可

代码:

bool cmp(vector<int> it1,vector<int> it2)
{
    return it1[1]<it2[1];
}
 
class Solution {
public:
    int findLongestChain(vector<vector<int>>& pairs) {    
        int res=0;
        vector<vector<int>>ans;
        vector<int>temp;
        ans.clear();        
        sort(pairs.begin(),pairs.end(),cmp);
        for(auto it=pairs.begin();it!=pairs.end();it++){
            temp.clear();
            temp.insert(temp.end(),(*it)[1]);
            temp.insert(temp.end(),1);
            for(auto it2=ans.begin();it2!=ans.end();it2++){
                if((*it)[0]>(*it2)[0] && temp[1]<(*it2)[1]+1){
                    temp[1]=(*it2)[1]+1;
                }
            }
            ans.insert(ans.end(),temp);
            if(res<temp[1]){
                res=temp[1];
            }
        }
        return res;
    }
};

还有一种贪心的算法:

以第二个数为第一关键字,第一个数为第二个关键字,进行排序。

 力扣 1235. 规划兼职工作

你打算利用空闲时间来做兼职工作赚些零花钱。

这里有 n 份兼职工作,每份工作预计从 startTime[i] 开始到 endTime[i] 结束,报酬为 profit[i]

给你一份兼职工作表,包含开始时间 startTime,结束时间 endTime 和预计报酬 profit 三个数组,请你计算并返回可以获得的最大报酬。

注意,时间上出现重叠的 2 份工作不能同时进行。

如果你选择的工作在时间 X 结束,那么你可以立刻进行在时间 X 开始的下一份工作。

示例 1:

输入:startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]
输出:120
解释:
我们选出第 1 份和第 4 份工作, 
时间范围是 [1-3]+[3-6],共获得报酬 120 = 50 + 70。

示例 2:

输入:startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]
输出:150
解释:
我们选择第 1,4,5 份工作。 
共获得报酬 150 = 20 + 70 + 60。

示例 3:

输入:startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]
输出:6

提示:

  • 1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4
  • 1 <= startTime[i] < endTime[i] <= 10^9
  • 1 <= profit[i] <= 10^4
//拓展数据域,加上id
template<typename T>
vector<pair<T, int>>expand(vector<T>v)
{
	vector<pair<T, int>>ans;
	ans.resize(v.size());
	for (int i = 0; i < v.size(); i++)ans[i].first = v[i], ans[i].second = i;
	return ans;
}
//提取pair数组的second
template<typename T1, typename T2>
vector<T2> fdraw2(vector<pair<T1, T2>>v)
{
	vector<T2>ans(v.size());
	for (int i = 0; i < v.size(); i++)ans[i] = v[i].second;
	return ans;
}
//给vector拓展,加上id并排序
template<typename T>
bool cmp(pair<T, int> x, pair<T, int> y)
{
	if (x.first == y.first)return x.second < y.second;
	return x.first < y.first;
}
template<typename T>
vector<pair<T, int>> sortWithId(vector<T>v)
{
	vector<pair<T, int>>ans = expand(v);
	sort(ans.begin(), ans.end(), cmp<T>);
	return ans;
}
//排序后数组中的每个数的原ID,输入8 5 6 7,输出1 2 3 0,也可以直接求逆置换
template<typename T>
vector<int> sortId(vector<T>v)
{
	return fdraw2(sortWithId(v));
}
//2个vector拼接起来
template<typename T>
vector<T> join(const vector<T>& v1, const vector<T>& v2)
{
	vector<T>ans(v1.size() + v2.size());
	copy(v1.begin(), v1.end(), ans.begin());
	copy(v2.begin(), v2.end(), ans.begin() + v1.size());
	return ans;
}
//把id数组转化为对应的数v[id]
template<typename T>
vector<T> fgetNumFromId(vector<T>& v, vector<int>id)
{
	vector<T>ans;
	ans.resize(id.size());
	for (int i = 0; i < id.size(); i++)ans[i] = (id[i] >= 0 && id[i] < v.size()) ? v[id[i]] : -1;
	return ans;
}
class Solution {
public:
	map<int,int> div(vector<int>v)
	{
		sort(v.begin(),v.end());
		map<int, int>ans;
		for (int i = 1; i < v.size(); i++)if (v[i] != v[i - 1])ans[v[i]] = ans[v[i - 1]] + 1;
		return ans;
	}
	int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
		vector<int>v = join(startTime,endTime);
		map<int, int> m = div(v);
		v = sortId(endTime);
		vector<int> id = fgetNumFromId(endTime, v);
		map<int, int>mans;
		mans[m[id[0]]] = profit[v[0]];
		for (int i = 1; i < v.size(); i++) {
			for (int j = m[id[i - 1]] + 1; j < m[id[i]]; j++)mans[j] = mans[m[id[i - 1]]];
			mans[m[id[i]]] = max(max(profit[v[i]] + mans[m[startTime[v[i]]]], mans[m[id[i - 1]]]), mans[m[id[i]]]);
		}
		return mans[m[id[v.size() - 1]]];
	}
};

 力扣 1340. 跳跃游戏 V

给你一个整数数组 arr 和一个整数 d 。每一步你可以从下标 i 跳到:

  • i + x ,其中 i + x < arr.length 且 0 < x <= d 。
  • i - x ,其中 i - x >= 0 且 0 < x <= d 。

除此以外,你从下标 i 跳到下标 j 需要满足:arr[i] > arr[j] 且 arr[i] > arr[k] ,其中下标 k 是所有 i 到 j 之间的数字(更正式的,min(i, j) < k < max(i, j))。

你可以选择数组的任意下标开始跳跃。请你返回你 最多 可以访问多少个下标。

请注意,任何时刻你都不能跳到数组的外面。

示例 1:

输入:arr = [6,4,14,6,8,13,9,7,10,6,12], d = 2
输出:4
解释:你可以从下标 10 出发,然后如上图依次经过 10 --> 8 --> 6 --> 7 。
注意,如果你从下标 6 开始,你只能跳到下标 7 处。你不能跳到下标 5 处因为 13 > 9 。你也不能跳到下标 4 处,因为下标 5 在下标 4 和 6 之间且 13 > 9 。
类似的,你不能从下标 3 处跳到下标 2 或者下标 1 处。

示例 2:

输入:arr = [3,3,3,3,3], d = 3
输出:1
解释:你可以从任意下标处开始且你永远无法跳到任何其他坐标。

示例 3:

输入:arr = [7,6,5,4,3,2,1], d = 1
输出:7
解释:从下标 0 处开始,你可以按照数值从大到小,访问所有的下标。

示例 4:

输入:arr = [7,1,7,1,7,1], d = 2
输出:2

示例 5:

输入:arr = [66], d = 1
输出:1

提示:

  • 1 <= arr.length <= 1000
  • 1 <= arr[i] <= 10^5
  • 1 <= d <= arr.length
class Solution {
public:
	map<int, int>m;
	int dp(vector<int>& arr, int d, int id)
	{
		if (m[id])return m[id];
		int ans = 1;
		for (int i = id - 1; i >= id - d; i--) {
			if (i < 0 || arr[i] >= arr[id])break;
			ans = max(ans, dp(arr, d, i) + 1);
		}
		for (int i = id + 1; i <= id + d; i++) {
			if (i >= arr.size() || arr[i] >= arr[id])break;
			ans = max(ans, dp(arr, d, i) + 1);
		}
		return m[id] = ans;
	}
	int maxJumps(vector<int>& arr, int d) {
		int ans = 0;
		for (int i = 0; i < arr.size(); i++)ans = max(ans, dp(arr, d, i));
		return ans;
	}
};

 力扣 1696. 跳跃游戏 VI(不能用备忘录)

给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。

一开始你在下标 0 处。每一步,你最多可以往前跳 k 步,但你不能跳出数组的边界。也就是说,你可以从下标 i 跳到 [i + 1, min(n - 1, i + k)] 包含 两个端点的任意位置。

你的目标是到达数组最后一个位置(下标为 n - 1 ),你的 得分 为经过的所有数字之和。

请你返回你能得到的 最大得分 。

示例 1:

输入:nums = [1,-1,-2,4,-7,3], k = 2
输出:7
解释:你可以选择子序列 [1,-1,4,3] (上面加粗的数字),和为 7 。

示例 2:

输入:nums = [10,-5,-2,4,0,3], k = 3
输出:17
解释:你可以选择子序列 [10,4,3] (上面加粗数字),和为 17 。

示例 3:

输入:nums = [1,-5,-20,4,-1,3,-6,-3], k = 2
输出:0

提示:

  •  1 <= nums.length, k <= 105
  • -104 <= nums[i] <= 104

因为性能的问题,不能只用递推式,还需要优先队列来优化,所以要按照严格的顺序来dp,即非备忘录写法。

map<int, int>m;
class Solution {
public:
	struct cmp {
		bool operator()(int x, int y) {
			return m[x] < m[y];
		}
	};
	priority_queue<int, vector<int>, cmp>q;
	void dp(vector<int>& nums, int k, int id) {
		if (id == nums.size() - 1) {
			q.push(id);
			m[id] = nums[id];
			return;
		}
		int x = q.top();
		while (x - id > k)q.pop(), x = q.top();
		m[id] = nums[id] + m[x];
		q.push(id);
	}
	int maxResult(vector<int>& nums, int k) {
		for (int i = nums.size() - 1; i >= 0; i--)dp(nums, k, i);
		return m[0];
	}
};

 力扣 1335. 工作计划的最低难度

你需要制定一份 d 天的工作计划表。工作之间存在依赖,要想执行第 i 项工作,你必须完成全部 j 项工作( 0 <= j < i)。

你每天 至少 需要完成一项任务。工作计划的总难度是这 d 天每一天的难度之和,而一天的工作难度是当天应该完成工作的最大难度。

给你一个整数数组 jobDifficulty 和一个整数 d,分别代表工作难度和需要计划的天数。第 i 项工作的难度是 jobDifficulty[i]。

返回整个工作计划的 最小难度 。如果无法制定工作计划,则返回 -1 。

示例 1:

输入:jobDifficulty = [6,5,4,3,2,1], d = 2
输出:7
解释:第一天,您可以完成前 5 项工作,总难度 = 6.
第二天,您可以完成最后一项工作,总难度 = 1.
计划表的难度 = 6 + 1 = 7 
示例 2:

输入:jobDifficulty = [9,9,9], d = 4
输出:-1
解释:就算你每天完成一项工作,仍然有一天是空闲的,你无法制定一份能够满足既定工作时间的计划表。
示例 3:

输入:jobDifficulty = [1,1,1], d = 3
输出:3
解释:工作计划为每天一项工作,总难度为 3 。
示例 4:

输入:jobDifficulty = [7,1,7,1,7,1], d = 3
输出:15
示例 5:

输入:jobDifficulty = [11,111,22,222,33,333,44,444], d = 6
输出:843
 

提示:

1 <= jobDifficulty.length <= 300
0 <= jobDifficulty[i] <= 1000
1 <= d <= 10

class Solution {
public:
	map<int, map<int, int>>m;
	int dp(vector<int>& v, int id, int d)
	{
		if (m[id][d])return m[id][d];
		if (d > id + 1)return 123456789;
		int ans = dp(v, id - 1, d - 1) + v[id];
		int x = v[id];
		for (int i = id - 1; i >= 0; i--) {
			x = max(x, v[i + 1]);
			ans = min(ans, dp(v, i, d-1) + x);
		}
		return m[id][d] = ans;
	}
	int minDifficulty(vector<int>& v, int d) {
		for (int i = 0; i < v.size(); i++) {
			v[i]++;
			m[i][1] = max(m[i - 1][1], v[i]);
		}
		int ans = dp(v, v.size() - 1, d);
		return ans == 123456789 ? -1 : ans - d;
	}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/590650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅科普一下计算机发展史阶段及那些不为人知的重要里程碑

目录 〇、前言 一、计算机发展历史阶段 二、计算机发展史中重要的里程碑 三、计算机对人类社会发展的重要性 四、计算机的应用领域 五、常见计算机辅助技术 六、总结 〇、前言 计算机的诞生无疑对人类社会的发展起着至关重要的巨大作用。计算机发明者名叫约翰冯诺依曼&a…

SAP-MM-发票-采购运费

采购运费是采购业务中一种特殊的定价&#xff0c;在SAP系统中&#xff0c;交货成本和其相近&#xff0c;是指在货物交付过程中发生的运输成本&#xff0c;只要有货物交付&#xff0c;就会有运费&#xff0c;而运费或者由采购方承担&#xff0c;或者由销售方承担&#xff0c;国内…

03SpringCloud Docker

Docker (1&#xff09;从VM与Docker框架中&#xff0c;直观上VM多了一层Guest OS&#xff0c;同时Hypervisor会对硬件资源进行虚拟化&#xff0c;docker直接使用硬件资源&#xff0c;所以资源利用率相对docker低。 &#xff08;2&#xff09;openstack能够以10台/min的速度创建…

SSM框架学习-拦截器

1. 简介 在Spring框架中&#xff0c;拦截器是一种很重要的组件&#xff0c;它们允许在请求到达控制器之前或之后执行一些代码。拦截器在请求处理的特定点进行拦截&#xff0c;然后通过逻辑来决定是否将控制器的处理传递给下一个处理程序。 在Spring中&#xff0c;拦截器是由实现…

【MATLAB速成】知识点总结(通俗易懂,学不会来打我)

【MATLAB速成】知识点总结&#xff08;通俗易懂&#xff0c;学不会来打我&#xff09; 一、概念 MATLAB的中文名称是&#xff08;矩阵实验室&#xff09;&#xff0c;英文全称是&#xff08;Matrix Laboratory&#xff09;&#xff0c;是一种以&#xff08;矩阵计算&#xff…

【学习日记2023.5.30】之 管理端订单模块完善_调用百度地图优化用户端提交订单是否超出配送距离

文章目录 9. 管理端订单模块完善9.1 需求分析和涉及9.2 代码开发Controller层Service层接口Service层实现类Mapper层 9.3 功能测试9.4 提交代码9.5 优化用户下单功能&#xff0c;引入距离判断 9. 管理端订单模块完善 订单搜索各个状态的订单数量统计查询订单详情接单拒单取消订…

古诗生成-pytorch

本文为RNN做古诗生成的一个小demo&#xff0c;只要是为了完成课上的作业&#xff08;由于训练比较慢&#xff0c;所以周期仅设置为3&#xff0c;大一点性能可能会更好&#xff09;&#xff0c;如有需要可以在这基础之上进行加工&#xff0c;数据集没办法上传&#xff0c;如有需…

FreeRTOS_从底层学习实时操作系统

目录 1. 裸机系统和多任务系统 2. 任务的定任务切换的实现 2.1 什么是任务&#xff1f; 2.2 调度器 2.3 临界段 3. 空闲任务和阻塞延迟 4. 时间片 1. 裸机系统和多任务系统 裸机系统&#xff1a; 裸机系统分为轮询系统和前后台系统&#xff1b;&#xff08;51单片机就属…

八大排序:直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序

文章目录 排序概念常见的排序算法常见排序算法的实现直接插入排序希尔排序选择排序堆排序冒泡排序快速排序递归实现Hoare版本挖坑法前后指针法 非递归实现Hoare版本挖坑法前后指针法 快速排序俩个优化 归并排序递归实现非递归实现外排序 计数排序 常见排序算法的性能分析 排序概…

【已完美解决】scons问题求助:如何设置编译输出目录搞清楚后,有些编译输出的obj文件却在源码目录,而不是设置的输出目录。

【已完美解决】scons问题求助&#xff1a;如何设置编译输出目录搞清楚后&#xff0c;有些编译输出的obj文件却在源码目录&#xff0c;而不是设置的输出目录。 文章目录 1 前置背景2 我的疑问3 一手点拨4 问题解决 1 前置背景 最近在基于目前已有的rt-thread构建框架&#xff0…

【Spring源码解读一】IoC容器之AnnotationConfigApplicationContext

根据AnnotationConfigApplicationContext类去阅读其将Bean对象交给IoC容器管理的过程。以下这三个代码块是将配置类注册进IoC容器的例子。下面是关于这个类的继承与实现的类图关系树。 public class Test {public static void main(String[] args) {// 配置类注册进IoC容器App…

解决Ubuntu16中安装opencv后找不到vtk库的问题

最近一个项目中要用到OpenCV的VTK库&#xff0c;但引入头文件#include <opencv2/viz.hpp>时却说找不到这个库&#xff0c;网上搜了下说在编译opencv源码的时候&#xff0c;需要加上编译VTK库的选项&#xff0c;于是重新下载、编译、安装了源码&#xff0c;在cmake时加上了…

最流行的AI绘图工具Midjourney,你不得不知道的使用技巧

​关注文章下方公众号&#xff0c;可免费获取AIGC最新学习资料 本文字数&#xff1a;1500&#xff0c;阅读时长大约&#xff1a;10分钟 Midjourney成为了最受欢迎的生成式AI工具之一。它的使用很简单。输入一些文本&#xff0c;Midjourney背后的大脑&#xff08;或计算机&#…

Linux 权限

目录 一、 从问题开始 问题一: 什么叫shell? 问题二: 为什么不能直接使用kernel呢? 问题三: shell 与bash 有什么不同吗? 二、 Linux权限 0x01 Linux用户 0x02 切换用户命令 0x03 sudo命令 0x04 权限的相关概念 0x05 chmod 0x06 chown 0x07 chgrp 0x08 文件权…

重磅!软著申请不需要邮寄纸质材料啦,附软著申请流程。

重磅&#xff01;软著申请不需要邮寄纸质材料啦&#xff0c;附软著申请流程。 最新消息申请流程一&#xff0c;准备申请材料二&#xff0c;申请人填写申请表三&#xff0c;提交申请材料四&#xff0c;补正五&#xff0c;审查六&#xff0c;发布公告七&#xff0c;接受异议八&am…

力扣---二叉树OJ题(多种题型二叉树)

文章目录 前言&#x1f31f;一、剑指 Offer 55 - I. 二叉树的深度&#x1f30f;1.1 链接&#xff1a;&#x1f30f;1.2 代码一&#xff1a;&#x1f30f;1.3 代码二&#xff1a;&#x1f30f;1.4 流程图&#xff1a; &#x1f31f;二、100. 相同的树&#x1f30f;2.1 链接&…

超强实用!利用xfsdump和xfsrestore打造无懈可击的数据备份与恢复策略

前言 上次我们分析了EXT文件系统的恢复方式&#xff0c;借助于extundelete工具仅可以恢复EXT类型的文件&#xff0c;但无法恢复CentOS 7系统&#xff0c;因为centos7默认采用xfs类型的文件。 xfs文件系统恢复工具有以下几种&#xff1a; xfsprogs&#xff1a;xfs文件系统扩展…

HTB MonitorsTwo

MonitorsTwo HTB MonitorsTwo 老规矩信息收集了&#xff1a; NMAP信息收集 ┌──(kali㉿kali)-[~/桌面] └─$ sudo nmap --min-rate 1000 10.10.11.211 Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-19 09:18 CST Nmap scan report for 10.10.11.211 Host is up…

Python入门(十六)函数(四)

函数&#xff08;四&#xff09; 1.传递列表1.1 在函数中修改列表 2.传递任意数量的实参2.1 结合使用位置实参和任意数量实参2.2 使用任意数量的关键字实参 作者&#xff1a;Xiou 1.传递列表 我们经常会发现&#xff0c;向函数传递列表很有用&#xff0c;其中包含的可能是名字…

设计模式-模板方法模式

模板方法模式 问题背景解决方案&#xff1a;模板方法模式基本介绍解决问题代码示例运行结果 钩子方法注意事项和细节 问题背景 豆浆的制作&#xff1a; 1&#xff09;制作豆浆的流程&#xff1a;选材—>添加配料—>浸泡—>放到豆浆机打碎 2&#xff09;通过添加不同…