2022-02-09每日刷题打卡
飞书——每日一题
剑指 Offer 38. 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = “abc”
输出:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]
排列组合,这里我用的是dfs,如果获得s的长度,我们要整的就是和是一样长度的用s的字符拼凑的字符串。每次dfs确定一个位置的字符,比如第一个位置我们可以先是a,再试b,再试c。每次取一个,然后找下一个位置,用没有用过的字符,如果一开始选的a,那后面就选b,c。以此类推,当确定的位置等于s的长度时我们就把这个字符串存入vector容器里。至于怎么把每个字符放在我们要的位置,我们可以从我们想要的位置遍历字符串,比如我们要的是第二个位置的字符,我们可以从第二个字符开始,每次把遍历到的字符和第二个字符交换位置,比如abcd,我们要整第二个位置,就从第二个位置b开始,然后以此把c和b交换,再把交换后的字符串送去下一层dfs,再把这两个字符交换回来,在把b和d交换……当然可能会有重复的情况,比如abbb,那么哪怕和后面的字符交换,第二个位置也一直是b,为此我们用一个哈希表去重,如果这个字符已经在第二个位置上用过了,我们就不用这个字符来交换,去遍历下一个字符。
class Solution {
public:
vector<string>res;
vector<string> permutation(string s) {
dfs(s,0);
return res;
}
void dfs(string s,int u)
{
if(u==s.size()-1)
{
res.push_back(s);
return;
}
map<int,int>mymap;
for(int i=u;i<s.size();i++)
{
if(mymap[s[i]]==1)continue;
mymap[s[i]]=1;
swap(s[i],s[u]);
dfs(s,u+1);
swap(s[i],s[u]);
}
}
};
AcWing——算法基础
2. 01背包问题 - AcWing题库
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V 用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
动态规划经典题,就是让我们判断怎么拿可以使拿到的物品价值最大。
我们准备一个动规数组f,长度为0~背包容积V,f[i]的意思是,当背包容积为V时,我们能拿到的最大价值货物。
动规我觉着和递归很像,都是把大问题拆成小问题,比如这里,我们虽然背包容积是v,但我们可以从容积为0开始整起,让我们知道容积为0时能获得的最大价值,然后是容积为1,再试容积为2……直到容积为n。怎么算容积为0,1,2……时获得的最大价值呢?我们可以遍历货物,当这个货物我们背包容积能放得下的时候,我们判断一下,是拿了它大还是我们本身价值就大,如果我们不拿,价值还是等于我们本身,如果我们拿了,我们的容积(设为m)就会减去这个货物的体积(设为v),此时我们的价值就可以这么算:现背包容积(设为n)所能获得的最大价值加上刚刚加入的货物的价值。比如有个货物体积为2,价值也为2,而且我们已经知道了,如果背包容积为0时获得的最大价值为0,那我们如果拿了这个货物,那么价值就是背包容积为0(2-2=0)时的最大价值:0,加上货物价值:2,即0+2=2。这就是通过小背包容积可得的最大价值获得大背包容积可得最大价值的方法。
#include<iostream>
using namespace std;
const int N=1010;
int f[N],w[N],v[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)cin>>v[i]>>w[i];
for(int i=0;i<n;i++)
for(int j=m;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
cout<<f[m]<<endl;
return 0;
}
3. 完全背包问题 - AcWing题库
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V 用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行两个整数 vi,wi 用空格隔开,分别表示第 i 种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10
这题和01背包不同的是,01背包只有拿和不拿两种结果,所以叫做01,这里的完全背包有三种可能性,拿、不拿、重复拿。
但代码其实和01背包很像,只不过01背包的代码是从大背包空间遍历到小背包空间,完全背包的代码是从货物空间遍历到背包空间。但本质上都是一样的,都是通过小背包空间能获得的最大价值求大背包空间能获得的最大价值。我们每次选一个货物,从能装下这个货物的空间开始遍历,比如我们选的第一个货物体积是1价值是2,那我们就从1背包空间开始(因为0空间也装不下,直接默认它获得最大价值为0),然后从1开始遍历到我们当前的背包容积,状态设定和01一样:f[i]的意思是,当背包容积为V时,我们能拿到的最大价值货物。我们先是1空间,然后判断是拿了它大还是不拿它价值更大:max(f[j-v[i]]+w[i],f[j]),j是背包容积,i是第i个货物。
我们模拟一下例题,这样直观一点。
先从第一个货物开始,遍历后每个空间(0~5)的最大价值为:0 2 4 6 8 10;
再选第二个货物,遍历后每个空间(0~5)的最大价值为:0 2 4 6 8 10;(比如在容积为4时,如果拿了第二个货物那最大价值就是f[4-2]+w[2]=4+4=8,不拿也是8,同理可得其它容积的最大价值)
再选第三个货物,遍历后最大价值:0 2 4 6 8 10;(比如在容积为4时,如果拿了第三个货物,那最大价值就是容积为4-3=1的最大价值+第三个货物的价值=6,显然小于不拿时的8,所以我们选择不拿)
最后选第四个货物,最大价值也是:0 2 4 6 8 10;(你可以自己推一下)
最后返回我们的容积:5时的最大价值,也就是10。
#include<iostream>
using namespace std;
const int N=1010;
int f[N],w[N],v[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)cin>>v[i]>>w[i];
for(int i=0;i<n;i++)
for(int j=v[i];j<=m;j++)
f[j]=max(f[j],f[j-v[i]]+w[i]);
cout<<f[m]<<endl;
return 0;
}
蓝桥杯——算法提高
算法提高 最长公共子序列
问题描述
给定两个字符串,寻找这两个字串之间的最长公共子序列。
输入格式
输入两行,分别包含一个字符串,仅含有小写字母。
输出格式
最长公共子序列的长度。
样例输入
abcdgh
aedfhb
样例输出
3
样例说明
最长公共子序列为a,d,h。
数据规模和约定
字串长度1~1000。
这题用的是动态规划的知识点,不过是二维的动态规划。
先看这个图,我们以一个字符串的大小为行数,另一个字符串的大小做列数来做出这个二维数组,然后每次比较,如果当前遍历到的text1字符不和text2上字符相等,我们就从上方或前方取一个最大值赋在格子里,如果相等,就把斜上方的值+1赋在当前格子里,最后返回最右下角的格子即可。
#include<iostream>
using namespace std;
#include<string>
#include<vector>
#include<algorithm>
const int N=1010;
int dp[N][N];
int main()
{
string str1, str2;
cin >> str1 >> str2;
int n = str1.size(), m = str2.size();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (str1[i-1] == str2[j-1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
cout << dp[n][m] << endl;
return 0;
}