A. Catch the Coin
思路:由题目条件可知我们最好的前进办法就是向那个点的方向走 ( x + 1 , y + 1 ) (x+1, y+1) (x+1,y+1),等 x x x坐标与目标点一致时再只对 y y y进行操作。由于每移动一步 y y y的坐标都会减1(注意这里我们移动完了目标点才会移动),所以目标点的坐标最小为-1,否则从原点出发永远无法追上。
时间复杂度: O ( n ) O(n) O(n)
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
for (int i = 0; i < n; i++) {
int x, y;
std::cin >> x >> y;
if (y >= -1) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
solve();
}
return 0;
}
B. Substring and Subsequence
思路:首先我们要明白,子串一定是答案串的一部分,接下来,我们要找出最多有多少子序列中出现的字符在这个子串中出现过(注意必须保持顺序的一致性)记为 m x mx mx,然后 l e n ( a ) + l e n ( b ) − m x len(a) + len(b) - mx len(a)+len(b)−mx即为答案。
时间复杂度: O ( ∣ a ∣ × ∣ b ∣ ) O(|a| \times |b|) O(∣a∣×∣b∣)
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
std::string a, b;
std::cin >> a >> b;
int ans = 1E9;
for (int p = 0; p < b.size(); p++) {
int res = a.size();
int k = 0;
for (int i = 0; i < a.size() && p + k < b.size(); i++) {
if (b[p + k] == a[i]) {
k++;
}
}
res += b.size() - k;
ans = std::min(ans, res);
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
C. Two Movies
思路:对于 − 1 , 0 , 1 -1,0,1 −1,0,1三种值,每个人对两部电影的评价如果不同我们肯定是要选评价更高的那个,答案一定更优。剩下的就是讨论每个人对于两部电影评价一致的情况,都是 0 0 0我们不需要考虑,因为对最终结果不造成影响。对 − 1 -1 −1的情况,我们进行一个转化,即将当前两部电影的评分都 − 1 -1 −1,然后将 − 1 -1 −1的投票变为 1 1 1,这两种情况是等价的。所以也就是,最终我们只需要考虑正向投票的情况就好了。最终情况要么把票全投给一个人,要么两个人轮流给票,结果取最小值。
时间复杂度: O ( n ) O(n) O(n)
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n), b(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
for (int i = 0; i < n; i++) {
std::cin >> b[i];
}
int sumA = 0, sumB = 0;
int countPos = 0;
for (int i = 0; i < n; i++) {
if (a[i] != b[i]) {
if (a[i] > b[i]) {
sumA += a[i];
} else {
sumB += b[i];
}
} else {
if (a[i] == -1) {
sumA--;
sumB--;
countPos++;
} else if (a[i] == 1) {
countPos++;
}
}
}
int ans = std::min({sumA + countPos, sumB + countPos, (sumA + sumB + countPos) >> 1});
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
D. Smithing Skill
思路:首先有一个比较显然的贪心就是,每次合成后立即融化掉可以得到2点经验值,同时也可以回收一部分材料,所以实际上我们只需要求最多可以合成多少次就好了。对于每一种合成的金属,实际上的代价为 a i − b i a_i - b_i ai−bi,所以我们肯定优先选择代价最小的金属进行合成。
根据数据范围,我们考虑对花费代价能合成的次数进行预处理,设 d p [ i ] dp[i] dp[i]为材料为 i i i时最多可以合成多少次,那么无论对于哪个 i i i,进行操作之后 i i i只会变小而不会变大,也就是说, d p [ i ] dp[i] dp[i]的求解仅依赖于之前的状态。在这之前我们需要先处理出材料为 i i i时最少需要花费多少材料能够进行一次合成,记为 f [ i ] f[i] f[i],于是 d p dp dp转移方程为 d p [ i ] = d p [ i − f [ i ] ] + 1 dp[i] = dp[i - f[i]] + 1 dp[i]=dp[i−f[i]]+1。
然后只需要查表求解次数就好了。
时间复杂度: O ( n + m ) O(n + m) O(n+m)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
int s = 0;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
s = std::max(s, a[i]);
}
std::vector<int> f(s + 1, s + 1);
for (int i = 0; i < n; i++) {
int b;
std::cin >> b;
f[a[i]] = std::min(f[a[i]], a[i] - b);
}
for (int i = 1; i <= s; i++) {
f[i] = std::min(f[i], f[i - 1]);
}
std::vector<int> dp(s + 1);
for (int i = 1; i <= s; i++) {
if (f[i] <= i) {
dp[i] = dp[i - f[i]] + 1;
}
}
i64 ans = 0;
while (m--) {
int c;
std::cin >> c;
if (c > s) {
int t = (c - s - 1 + f[s]) / f[s];
ans += t;
c -= t * f[s];
}
ans += dp[c];
}
std::cout << ans * 2 << "\n";
return 0;
}