A - Frog 1
大意
有块石头,第
块石头的高度为
。从石头
跳到石头
的花费是
。
一只青蛙在石头上,每次可以跳
步或
步,请问跳到石头
的最小代价是多少?
思路
设,
为青蛙跳到第
号石头时的最小代价。
每一个点都可以由前两个点转移而来,因此状态转移方程为:
边界可由定义得出:。
时间复杂度
代码
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
#define int long long
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
vector<int> a(n), dp(n, 0);
for(auto &i: a) cin >> i;
auto cost = [&](int i, int j) -> int{
return abs(a[i] - a[j]);
};
dp[1] = cost(0, 1);
for(int i = 2; i < n; i++)
dp[i] = min(dp[i - 1] + cost(i, i - 1), dp[i - 2] + cost(i, i - 2));
cout << dp.back() << endl;
return 0;
}
B - Frog 2
大意
有块石头,第
块石头的高度为
。从石头
跳到石头
的花费是
。
一只青蛙在石头上,每次可以跳
~
步,请问跳到石头
的最小代价是多少?
思路
和上一题一样,设,
为青蛙跳到第
号石头时的最小代价。
每一个点都可以由前个点转移而来(除了
的情况),因此状态转移方程为:
边界为。
时间复杂度
代码
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
#define int long long
const int INF = 0x3f3f3f3f;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, k;
cin >> n >> k;
vector<int> a(n), dp(n, INF);
for(auto &i: a) cin >> i;
auto cost = [&](int i, int j){
return abs(a[i] - a[j]);
};
dp[0] = 0;
for(int i = 1; i < n; i++){
for(int j = max(0LL, i - k); j < i; j++) dp[i] = min(dp[i], dp[j] + cost(i, j));
}
cout << dp.back() << endl;
return 0;
}
C - Vacation
大意
有天时间和
种活动。每天只能进行一个活动,且相邻两天不能做相同的活动。
第天做三种活动分别可以获得
点快乐值,求这
天里最大可以获得多少点快乐值。
思路
设为考虑到第
天,且第
天进行活动
的最大值。
由于相邻两天不能做相同的活动,所以只能从另外两个状态中的最大值转移过来。、
因此,状态转移方程为:
边界为,答案为
。
时间复杂度。
代码
#include <iostream>
#include <vector>
using namespace std;
#define int long long
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
vector<int> a(n), b(n), c(n);
vector<int> dp1(n, 0), dp2(n, 0), dp3(n, 0);
for(int i = 0; i < n; i++) cin >> a[i] >> b[i] >> c[i];
dp1[0] = a[0], dp2[0] = b[0], dp3[0] = c[0];
for(int i = 1; i < n; i++){
dp1[i] = max(dp2[i - 1], dp3[i - 1]) + a[i];
dp2[i] = max(dp1[i - 1], dp3[i - 1]) + b[i];
dp3[i] = max(dp1[i - 1], dp2[i - 1]) + c[i];
}
cout << max(dp1.back(), max(dp2.back(), dp3.back())) << endl;
return 0;
}
D - Knapsack 1
大意
有件物品和一个承重量为
千克的袋子。
第件物品的重量为
千克,价值为
元。
现在要挑选一些物品装入袋子里,求选择的物品的最大总价值。
思路
经典01背包问题,按照01背包的解法做即可。
注意需要long long。
时间复杂度
代码
#include <iostream>
#include <vector>
using namespace std;
#define int long long
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> w(n), v(n), dp(m + 1, 0);
for(int i = 0; i < n; i++) cin >> w[i] >> v[i];
for(int i = 0; i < n; i++)
for(int j = m; j >= w[i]; j--)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
cout << dp[m] << endl;
return 0;
}
E - Knapsack 2
大意
有件物品和一个承重量为
千克的袋子。
第件物品的重量为
千克,价值为
元。
现在要挑选一些物品装入袋子里,求选择的物品的最大总价值。
思路
很大,直接套用01背包计算,时间和空间肯定一定炸。
由于很小,我们可以转换思路,设
为价值为
的最小重量,这样就不会炸了。
最后从大到小遍历,找到符合
的直接输出
。
时间复杂度,其中
为
的取值范围,
代码
#include <iostream>
#include <vector>
using namespace std;
#define int long long
const int INF = 0x3f3f3f3f;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> w(n), v(n);
int sum = 0;
for(int i = 0; i < n; i++){
cin >> w[i] >> v[i];
sum += v[i];
}
vector<int> dp(sum + 1, INF);
dp[0] = 0;
for(int i = 0; i < n; i++)
for(int j = sum; j >= v[i]; j--)
dp[j] = min(dp[j], dp[j - v[i]] + w[i]);
for(int j = sum; j >= 0; j--)
if(dp[j] <= m){
cout << j << endl;
break;
}
return 0;
}
F - LCS
大意
给定两个字符串,输出它们的任意一个最长公共子序列。
思路
求LCS时将最优策略保存下来,用来表示状态
是否和
有关,
来表示状态
是否和
有关。
根据和
,就可以递归输出方案。
代码
#include <iostream>
#include <vector>
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
string x, y;
cin >> x >> y;
int n = x.size(), m = y.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1));
vector<vector<int>> f1(n + 1, vector<int>(m + 1));
vector<vector<int>> f2(n + 1, vector<int>(m + 1));
auto lcs = [&](string a, string b, int n, int m) -> void{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
if(a[i - 1] == b[j - 1]){
dp[i][j] = dp[i - 1][j - 1] + 1;
f1[i][j] = 1;
f2[i][j] = 1;
}else if(dp[i - 1][j] > dp[i][j - 1]){
dp[i][j] = dp[i - 1][j];
f1[i][j] = 1;
}else{
dp[i][j] = dp[i][j - 1];
f2[i][j] = 1;
}
}
};
string ans = "";
auto dfs = [&](auto self, string a, string b, int i, int j) -> void{
if(i == 0 || j == 0) return;
self(self, a, b, i - f1[i][j], j - f2[i][j]);
if(a[i - 1] == b[j - 1]) ans.push_back(a[i - 1]);
};
lcs(x, y, n, m);
dfs(dfs, x, y, n, m);
cout << ans << endl;
}
G - Longest Path
大意
给定一个DAG,求其最长链上的边数量。
思路
DP求解,对每个点进行记忆化搜索,求出每个点为起点的最长链。
最后取个max即可。
代码
#include <iostream>
#include <vector>
using namespace std;
#define int long long
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector<vector<int>> G(n);
vector<int> dp(n, 0);
while(m--){
int u, v;
cin >> u >> v;
u--, v--;
G[u].push_back(v);
}
auto dfs = [&](auto self, int u) -> int{
if(dp[u] == 0)
for(int v : G[u]) dp[u] = max(dp[u], self(self, v) + 1);
return dp[u];
};
int ans = 0;
for(int i = 0; i < n; i++) ans = max(ans, dfs(dfs, i));
cout << ans << endl;
return 0;
}