搜索练习题及参考代码
01爬楼梯
问题描述
给你一个整数数组 cost
,其中 cost[i]
是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
2 <= cost.length <= 1000
0 <= cost[i] <= 999
输入描述
第一行输入一个整数n
表示数组的长度
第二行是n
个由空格分割的整数,代表数组cost
输出描述
输出一个整数代表达到楼梯顶部的最低花费
输入样例
3
10 15 20
输出样例
15
参考代码
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n;
cin>>n;
vector<int> cost(n);
for(int i=0;i<n;i++){
cin>>cost[i];
}
vector<int> dp(n+1);
dp[0] = 0;
dp[1] = 0;
for(int i=2;i<=n;i++){
dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]) ;
}
cout<<dp[n]<<endl;
return 0;
}
02连续子数组的最大和
问题描述
输入一个整型数组arr
,数组中的一个或连续多个整数组成一个子数组。
求所有子数组的和的最大值。
1 <= arr.length <= 10^5
-10000 <= arr[i] <= 10000
输入描述
第一行输入一个整数n
表示数组的长度
第二行是n
个由空格分割的整数,代表数组arr
输出描述
输出一个整数代表达所有子数组的和的最大值
输入样例
9
-2 1 -3 4 -1 2 1 -5 4
输出样例
6
参考代码
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n;
cin>>n;
vector<int> arr(n);
for(int i=0;i<n;i++){
cin>>arr[i];
}
int dp=0, res=arr[0];
for(int i=0;i<n;i++){
dp = max(dp+arr[i], arr[i]);
res = max(res, dp);
}
cout<<res<<endl;
return 0;
}
状态:dp[i]
表示以第 i
个数结尾的连续子数组的最大和;
状态转移函数:dp[i]=max(dp[i−1]+nums[i],nums[i])
。
边界条件:dp[0]=nums[0]
03掷骰子的N种方法
问题描述
这里有 n
个一样的骰子,每个骰子上都有 k
个面,分别标号为 1
到 k
。
给定三个整数 n
, k
和 target
,返回有多少种可能的滚动骰子的方式,使正面朝上的数字之和等于 target
。
答案可能很大,你需要对 1 0 9 + 7 10^9 + 7 109+7 取模 。
1 <= n, k <= 30
1 <= target <= 1000
输入描述
第一行输入三个用空格分隔的整数 n
,k
和target
输出描述
输出一个整数代表答案
输入样例
2 6 7
输出样例
6
参考代码
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n, k, target;
cin>>n>>k>>target;
vector<vector<int>> dp(n+1, vector<int>(target+1));
int m=min(k,target);
for(int i=1;i<=m;i++){
dp[1][i]=1;
}
for(int i=2;i<=n;i++){
for(int j=i;j<=target;j++){
for(int tk=1;j-tk>=0 && tk<=k;tk++){
dp[i][j] = (dp[i][j] + dp[i-1][j-tk]) % 1000000007;
}
}
}
cout<<dp[n][target]<<endl;
return 0;
}
状态:dp[i][j]
代表 扔 i
个骰子和为 j
;
状态转移函数:dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j - 2] + ... + dp[i - 1][j-k]
边界条件: dp[1][k] = 1 ( 1<= k <= min(target, k) )
04戳气球
问题描述
有 n
个气球,编号为0
到 n - 1
,每个气球上都标有一个数字,这些数字存在数组 nums
中。
现在要求你戳破所有的气球。戳破第 i
个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1]
枚硬币。 这里的 i - 1
和 i + 1
代表和 i
相邻的两个气球的序号。如果 i - 1
或 i + 1
超出了数组的边界,那么就当它是一个数字为 1
的气球。
求所能获得硬币的最大数量。
n == nums.length
1 <= n <= 300
0 <= nums[i] <= 100
输入描述
第一行输入一个整数n
表示数组的长度
第二行是n
个由空格分割的整数,代表数组nums
输出描述
输出一个整数代表答案
输入样例
2
1 5
输出样例
10
参考代码
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n;
cin>>n;
vector<int> nums(n);
for(int i=0;i<n;i++){
cin>>nums[i];
}
vector<vector<int>> dp(n + 2, vector<int>(n + 2));
vector<int> val(n + 2);
val[0] = val[n + 1] = 1;
for (int i = 1; i <= n; i++) {
val[i] = nums[i - 1];
}
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 2; j <= n + 1; j++) {
for (int k = i + 1; k < j; k++) {
int sum = val[i] * val[k] * val[j];
sum += dp[i][k] + dp[k][j];
dp[i][j] = max(dp[i][j], sum);
}
}
}
cout<<dp[0][n + 1]<<endl;
return 0;
}
戳气球的操作会导致两个气球从不相邻变成相邻,使得后续操作难以处理。于是我们可以倒过来看这些操作,将全过程看作是每次添加一个气球。
状态:dp[i][j]
表示填满开区间 (i,j)
能得到的最多硬币数;
状态转移函数:dp[i][j] = max(nums[i]*nums[k]*nums[j]+dp[i][k]+dp[k][j]) ( i+1<=k<=j-1, i<j-1)
边界条件: dp[i][j] = 0 ( i>= j-1 )
05传递信息
问题描述
小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:
-
有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0
-
每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。
-
每轮信息必须需要传递给另一个人,且信息可重复经过同一个人
给定总玩家数 n
,以及按 [玩家编号,对应可传递玩家编号]
关系组成的二维数组 relation
。返回信息从小 A (编号 0 ) 经过 k
轮传递到编号为 n-1
的小伙伴处的方案数;若不能到达,返回 0。
2 <= n <= 10
1 <= k <= 5
1 <= relation.length <= 90, 且 relation[i].length == 2
0 <= relation[i][0],relation[i][1] < n 且 relation[i][0] != relation[i][1]
输入描述
第一行输入三个整数n
,k
和m
分别表示玩家数,轮数和连接数
接下来m
行,每行是2个由空格分割的整数,代表数组relation
输出描述
输出一个整数代表答案
输入样例
3 2 2
0 2
2 1
输出样例
0
参考代码
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n, k, m;
cin>>n>>k>>m;
vector<vector<int>> relation(m, vector<int>(2));
for(int i=0;i<m;i++){
cin>>relation[i][0]>>relation[i][1];
}
vector<int> dp(n);
dp[0] = 1;
for (int i = 0; i < k; i++) {
vector<int> next(n);
for (auto& edge : relation) {
int src = edge[0], dst = edge[1];
next[dst] += dp[src];
}
dp = next;
}
cout<<dp[n - 1]<<endl;
return 0;
}
06俄罗斯套娃信封
问题描述
给你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。
当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算 最多能有多少个 信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
注意:不允许旋转信封。
1 <= envelopes.length <= 10^5
envelopes[i].length == 2
1 <= wi, hi <= 10^5
输入描述
第一行输入一个整数n
表示信封数
接下来n
行,每行是2个由空格分割的整数,代表信封的宽度和高度
输出描述
输出一个整数代表答案
输入样例
4
5 4
6 4
6 7
2 3
输出样例
3
参考代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
bool cmp(vector<int> a, vector<int> b){
return a[0] < b[0] || (a[0] == b[0] && a[1] > b[1]);
}
int main(){
int n;
cin>>n;
vector<vector<int>> envelopes(n, vector<int>(2));
for(int i=0;i<n;i++){
cin>>envelopes[i][0]>>envelopes[i][1];
}
sort(envelopes.begin(), envelopes.end(), cmp);
vector<int> f(n, 1);
for (int i = 1; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (envelopes[j][1] < envelopes[i][1]) {
f[i] = max(f[i], f[j] + 1);
}
}
}
cout<<*max_element(f.begin(), f.end())<<endl;
return 0;
}
07编辑距离
问题描述
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
-
插入一个字符
-
删除一个字符
-
替换一个字符
-
0 <= word1.length, word2.length <= 500
-
word1
和word2
由小写英文字母组成
输入描述
输入两行,每行一个单词。
输出描述
输出一个整数代表答案
输入样例
horse
ros
输出样例
3
参考代码
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){
string word1, word2;
cin>>word1>>word2;
int n = word1.length();
int m = word2.length();
// 有一个字符串为空串
if (n * m == 0) return n + m;
// DP 数组
vector<vector<int>> D(n + 1, vector<int>(m + 1));
// 边界状态初始化
for (int i = 0; i < n + 1; i++) {
D[i][0] = i;
}
for (int j = 0; j < m + 1; j++) {
D[0][j] = j;
}
// 计算所有 DP 值
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < m + 1; j++) {
int left = D[i - 1][j] + 1;
int down = D[i][j - 1] + 1;
int left_down = D[i - 1][j - 1];
if (word1[i - 1] != word2[j - 1]) left_down += 1;
D[i][j] = min(left, min(down, left_down));
}
}
cout<<D[n][m]<<endl;
return 0;
}