Cards and Joy 题解
题意:
有
导入:
最容易想到的就是暴搜前
代码:TLE の 记录
显而易见,肯定超时。这时候,就要考虑优化了:对于每个相同的
分析:
为了真正的优化时间,就只能优化状态和改变处理方式了。首先,要明白一个显而易见的性质:假设当前是第
根据上述结论,就会延伸出一个问题:如何保证每个人都选了
为了更方便的理解问题,我们可以只考虑其中一组最多能达到的幸福值,然后再将每个组的答案相加。为了能更清楚的表现出当前的局面,因此不妨考虑状态为
每次转移就是当前这个人选择了
对于每一个相同的
算法:搜索。
时间复杂度:
不会去重导致几乎全部超时:TLE の 记录
思路:
这题的状态图一看就是有拓扑序的,因此可以动态规划。首先优化一下状态,考虑状态分组。可以发现,对于每一个
由于我们并不知道每次转移是不是最优的,因此转移是无法优化的。
状态数量依旧是
状态转移方程:
代码:
#include <algorithm>
#include <iostream>
#include <unordered_map>
using namespace std;
const int MAXN = 5005;
int n, k, v[MAXN], ans, dp[MAXN][MAXN];
unordered_map<int, int> a, b;
int main() {
ios::sync_with_stdio(0);
cin >> n >> k;
for (int i = 1, x; i <= n * k; i++) {
cin >> x, a[x]++; // 值为x的数字出现的次数
}
for (int i = 1, x; i <= n; i++) {
cin >> x, b[x]++; // 幸运数字为x的人出现的次数,等同于分组
}
for (int i = 1; i <= k; i++) {
cin >> v[i];
}
for (auto i : b) { // 对于每个组
for (int j = 1; j <= i.second; j++) { // 枚举人
for (int x = 1; x <= a[i.first]; x++) { // 枚举数字
for (int y = 1; y <= min(x, k); y++) { // 枚举选了多少数字
dp[j][x] = max(dp[j][x], dp[j - 1][x - y] + v[y]);
}
}
}
ans += dp[i.second][a[i.first]];
fill(dp[0], dp[n] + k + 1, 0);
}
cout << ans;
return 0;
}
拓展延伸
实际上我们并不需要对于每个分组都进行考虑,因为真正影响答案的是数出现的次数而不是值。因此我们可以预处理出前
代码
#include <algorithm>
#include <iostream>
#include <unordered_map>
using namespace std;
const int MAXN = 5005;
int n, k, v[MAXN], ans, dp[MAXN][MAXN];
unordered_map<int, int> a, b;
int main() {
ios::sync_with_stdio(0);
cin >> n >> k;
for (int i = 1, x; i <= n * k; i++) {
cin >> x, a[x]++;
}
for (int i = 1, x; i <= n; i++) {
cin >> x, b[x]++;
}
for (int i = 1; i <= k; i++) {
cin >> v[i];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n * k; j++) {
for (int x = 1; x <= min(j, k); x++) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - x] + v[x]);
}
}
}
for (auto i : b) {
ans += dp[i.second][a[i.first]];
}
cout << ans;
return 0;
}