ABC331 部分题解 附英文版

中文版

A

题目大意

一年有 \(M\) 个月,一个月有 \(D\) 天。现在的时间为 \(y\)\(m\)\(d\) 日,请问明天是多少?

思路

首先,既然要计算明天,那么我们直接将 \(d\)\(1\) 就行了,然后检查一下 \(d\) 是否超出了 \(D\) 的范围,如果超出了,那么就将 \(d\) 减去 \(D\)\(m\)\(1\)。再检查 \(m\),如果超出了 \(M\) 的范围,那么就把 \(m\) 减去 \(M\)\(y\) 加上 \(1\)。最后输出 \(y,m,d\) 即可。

代码

#include <iostream>

using namespace std;

int M, D, y, m, d;

int main() {
  cin >> M >> D >> y >> m >> d;
  d++, d > D && (d = 1, m++);
  m > M && (m = 1, y++);
  cout << y << ' ' << m << ' ' << d << '\n';
  return 0;
}

B

题目大意

\(6\) 个鸡蛋 \(S\) 元,\(8\) 个鸡蛋 \(M\) 元,\(12\) 个鸡蛋 \(L\) 元。问你买至少 \(N\) 个鸡蛋需要花多少元。

思路

其实这题过于水了,\(N\) 只有 \(100\),完全可以双重循环暴力。但是不知道为什么,我的手就是不听使唤,竟然打出了一个 01 背包!设 \(dp_i\) 表示为购买 \(i\) 个鸡蛋最少需要多少元,那么照着思路转移就行了。最后取上一个 \(\min\)

代码

#include <iostream>
#include <algorithm>

using namespace std;

const int kMaxN = 1005;

int dp[kMaxN], n, s, m, l;

int main() {
  fill(dp + 1, dp + kMaxN, 1e9);
  cin >> n >> s >> m >> l;
  for (int i = 1; i <= n + 15; i++) {
    if (i >= 6) {
      dp[i] = min(dp[i], dp[i - 6] + s);
    }
    if (i >= 8) {
      dp[i] = min(dp[i], dp[i - 8] + m);
    }
    if (i >= 12) {
      dp[i] = min(dp[i], dp[i - 12] + l);
    }
  }
  cout << *min_element(dp + n, dp + n + 12 + 1) << '\n';
  return 0;
}

C

题目大意

给我一个长度为 \(N\) 的序列 \(A\),第 \(i\) 个数的值为 \(A_i\)。对于 \(A_i\),你需要求出比 \(A_i\) 大的数的和。

思路

我们首先将数组排序,然后在离散化一下,因为这题需要输出所有的答案。排完序之后,我们就从后往前做一个后缀和,因为 \(A_{(i+1)}\) 一定大于等于 \(A_i\)。注意这里需要特殊处理一下等于的情况,使用 \(l\) 记录等于了几个数,\(x\) 表示等于的是几。然后统计到 \(ans\) 数组就行了。

注意这题要开 long long,我就是因为这里 WA 了。

代码

#include <iostream>
#include <algorithm>

using namespace std;
using ll = long long;

const ll kMaxN = 2e5 + 5;

struct Node {
  ll v, id;
} a[kMaxN];

ll p[kMaxN], ans[kMaxN], n, l, x;

int main() {
  cin >> n;
  for (ll i = 1; i <= n; i++) {
    cin >> a[i].v, a[i].id = i;
  }
  sort(a + 1, a + n + 1, [](const Node &a, const Node &b) {
    return a.v < b.v;
  });
  p[n] = 0;
  for (ll i = n - 1; i >= 1; i--) {
    if (a[i].v == a[i + 1].v) {
      p[i] = p[i + 1];
      l++, x = a[i].v;
    } else {
      p[i] += p[i + 1] + a[i + 1].v + l * x;
      l = x = 0;
    }
  }
  for (ll i = 1; i <= n; i++) {
    ans[a[i].id] = p[i];
  }
  for (ll i = 1; i <= n; i++) {
    cout << ans[i] << ' ';
  }
  return 0;
}

E

题目大意

\(N\) 个主菜,第 \(i\) 个主菜需要 \(a_i\) 元;\(M\) 个配菜,第 \(i\) 个配菜 \(b_i\) 元。但是有 \(L\) 对主菜和配菜是不能配在一起的。一对菜的价值定义为主菜加上配菜价钱的和。问你最贵的一对菜需要多少元。

思路

我们先考虑一个暴力算法:首先枚举 \(N\) 个主菜,然后枚举 \(M\) 个配菜,最后遍历 \(L\) 个配对,判断一下是否可以购买,然后记录最大值。这种算法的时间复杂度是 \(\mathcal O(NML)\) 的,而 \(1\le N,M,L\le 10^5\),使用这种算法会直接吃席。

我们可以来尝试优化一下,既然要求的是最大值,那么一个主菜必须配对可以买的的最大价值的配菜。那么我们可以直接对 \(b\) 数组排序,然后从后往前遍历一遍,在来判断是否可以购买,如果可以就记录最大值,然后 break。这种做法虽然是三重循环,但是时间复杂度是 \(\mathcal O(L^2)\) 的,因为第一二层循环只会枚举 \(L\) 次,枚举完就必定可以找到可以配对的。

现在我们要加快判断是否能够购买的速度。可以使用一个集合,然后第三重循环就可以直接省掉——变成一个判断是否在集合内出现过的问题。这种算法的时间复杂度是 \(\mathcal O(L\log_2\min(M,L))\) 的,因为一个集合内的元素最多有 \(L\) 个,但是其实同样不能超过 \(M\) 个,因此这题就过掉了。

代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <set>

using namespace std;

int n, m, l, ans;

int main() {
  cin >> n >> m >> l;
  vector<int> a(n + 1);
  vector<pair<int, int>> b(m + 1);
  vector<set<int>> s(n + 1, set<int>());
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  for (int i = 1; i <= m; i++) {
    cin >> b[i].first, b[i].second = i;
  }
  sort(b.begin(), b.end(), [](const auto &a, const auto &b) {
    return a.first < b.first;
  });
  for (int i = 1, x, y; i <= l; i++) {
    cin >> x >> y;
    s[x].insert(y);
  }
  for (int i = 1; i <= n; i++) {
    for (int j = m; j >= 1; j--) {
      if (!s[i].count(b[j].second)) {
        ans = max(ans, a[i] + b[j].first);
        break;
      }
    }
  }
  cout << ans << '\n';
  return 0;
}

English

B

Problem

There are \(M\) months in a year, and \(D\) days in a month. What day follows year \(y\), month \(m\), day \(d\) ?

Solution

First, let's add \(d\) by \(1\). Then, if \(d\) is greater than or equal to \(D\),then \(d\) is subtracted from \(d\) and \(m\) is added by \(1\). Next, let's just calculate m in the same way. paying attention to the carry.

Code

#include <iostream>

using namespace std;

int M, D, y, m, d;

int main() {
  cin >> M >> D >> y >> m >> d;
  d++, d > D && (d = 1, m++);
  m > M && (m = 1, y++);
  cout << y << ' ' << m << ' ' << d << '\n';
  return 0;
}

B

Problem

A pack of \(6\) eggs costs \(S\) points, a pack of \(8\) eggs costs \(M\) points, and a pack of \(12\) costs \(L\) points. How many points do you need to buy more than \(n\) eggs?

Solution

This problem is very watery, 'cause \(n\) is only \(100\). It's perfectly possible to use a triple loop to go violent. We can use the idea of dynamic programming to solve this problem. Let \(dp_i\) indicate the minimum number of points needed to buy \(i\) eggs, then we can follow the idea of state transfer.

Code

#include <iostream>
#include <algorithm>

using namespace std;

const int kMaxN = 1005;

int dp[kMaxN], n, s, m, l;

int main() {
  fill(dp + 1, dp + kMaxN, 1e9);
  cin >> n >> s >> m >> l;
  for (int i = 1; i <= n + 15; i++) {
    if (i >= 6) {
      dp[i] = min(dp[i], dp[i - 6] + s);
    }
    if (i >= 8) {
      dp[i] = min(dp[i], dp[i - 8] + m);
    }
    if (i >= 12) {
      dp[i] = min(dp[i], dp[i - 12] + l);
    }
  }
  cout << *min_element(dp + n, dp + n + 12 + 1) << '\n';
  return 0;
}

C

Problem

You get an array \(a\) of length \(n\). For arbitrary \(a_i\), you need to find the sum of numbers greater than \(a_i\).

Solution

Let's sort the array \(a\) first, and do a discretization, 'cause this problem needs to output all the answers. Then, do a suffix sum on array \(a\) from back to front, 'cause \(a_{(i+1)}\) must be greater than \(a_i\). Note that there is a special need to deal with the case of equals, using \(l\) to record several numbers equals, and \(x\) to indicate the how many equals are. Finally, it's counted in the \(ans\) array.

Note that you need to use the long long for this problem, otherwise you'll find the WA.

Code

#include <iostream>
#include <algorithm>

using namespace std;
using ll = long long;

const ll kMaxN = 2e5 + 5;

struct Node {
  ll v, id;
} a[kMaxN];

ll p[kMaxN], ans[kMaxN], n, l, x;

int main() {
  cin >> n;
  for (ll i = 1; i <= n; i++) {
    cin >> a[i].v, a[i].id = i;
  }
  sort(a + 1, a + n + 1, [](const Node &a, const Node &b) {
    return a.v < b.v;
  });
  p[n] = 0;
  for (ll i = n - 1; i >= 1; i--) {
    if (a[i].v == a[i + 1].v) {
      p[i] = p[i + 1];
      l++, x = a[i].v;
    } else {
      p[i] += p[i + 1] + a[i + 1].v + l * x;
      l = x = 0;
    }
  }
  for (ll i = 1; i <= n; i++) {
    ans[a[i].id] = p[i];
  }
  for (ll i = 1; i <= n; i++) {
    cout << ans[i] << ' ';
  }
  return 0;
}

E

Problem

There are \(N\) types of main dishes, the \(i\)-th dish costs \(a_i\) price. And there are \(M\) types side dishes, the \(i\)-th dish costs \(b_i\) price. A main dish and a side dish can make up a set meal, and the price is the sum of the main dish and side dish. But there are \(L\) pairs of main dish and side dish that can't be combined into a set meal. You need to find maximum value for a set meal.

Solution

Let's start by considering a brute force algorithm: First enumerate \(N\) main dishes, then \(M\) side dishes, then iterate through \(L\) pairings, and record the maximum if you can buy it. But the time complexity is \(\mathcal O(NML)\), and \(1\le N,M,L\le 10^5\). friends can go to your house for your white dinner if using this algorithm.

We can try to optimize it. since the maximum value is required, then a main dish must be paired with the maximum value side dish that can be bought. We can sort the array \(b\) directly, and then iterate through it from back to front, to determine if it can be purchased. If it can, to record the maximum value, and then to break. Although this method is a triple loop, the time complexity is \(\mathcal O(L^2)\), 'cause the first and second layer loops will only enumerate \(L\) times, and after the enumeration, you must be able to find a pair of them.

Now we need to speed up the process of judging whether we can buy or not. You can use a set, and then the third loop can be simple omitted - it becomes a problem of determining whether or not is has ever appeared in the collection. The time complexity of this algorithm is \(\mathcal O(L\log_2\min(M,L))\), 'cause there are at most \(L\) elements in a set, but they can't exceed \(M\) either, so this problem is passed.

Code

#include <iostream>
#include <algorithm>
#include <vector>
#include <set>

using namespace std;

int n, m, l, ans;

int main() {
  cin >> n >> m >> l;
  vector<int> a(n + 1);
  vector<pair<int, int>> b(m + 1);
  vector<set<int>> s(n + 1, set<int>());
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  for (int i = 1; i <= m; i++) {
    cin >> b[i].first, b[i].second = i;
  }
  sort(b.begin(), b.end(), [](const auto &a, const auto &b) {
    return a.first < b.first;
  });
  for (int i = 1, x, y; i <= l; i++) {
    cin >> x >> y;
    s[x].insert(y);
  }
  for (int i = 1; i <= n; i++) {
    for (int j = m; j >= 1; j--) {
      if (!s[i].count(b[j].second)) {
        ans = max(ans, a[i] + b[j].first);
        break;
      }
    }
  }
  cout << ans << '\n';
  return 0;
}
posted @ 2023-12-03 22:12  haokee  阅读(118)  评论(0)    收藏  举报