赌局
如果我们已经选择了一种方案(以下 表示已选的),那么我们可以枚举最终是哪个人赢了,赌除了这个人以外的人的就要给你钱,赌了这个人的你就要给他钱,因此最坏情况下收益为
很显然 求和那里不好处理,但是我们可以做容斥,用所有 的和减去 ,因此问题变成
而已选的 的和在统计答案时其实是常数,因此我们需要最大化 ,即求
但是这个 很难处理,因为我们并不清楚三个当中的哪一个才是 。而我们选择一个新的元素时,可能会改变 ,也会导致 变化,这个变化是很难维护的。
因此我们需要将 作为限制,也就是限制三组当中的任何一组都要满足 不超过这个值,而在满足条件的情况下最大化 。
于是我们可以对每一组做 0/1 背包。将 视为重量, 视为价值,这样子我们就可以得到在重量不超过一定值的情况下,价值的最大值,也就是上式的要求。
因此思路就很清晰了。
- 对每一组做以 为重量, 为价值的 0/1 背包;
- 对背包做前缀 ,得到重量不超过限制的最大价值(如果 dp 数组全初始化为 就可以省略这一步);
- 枚举 的最大值,在每一组都不超过这个限制的情况下计算最大 和。
时间复杂度 。
#include <iostream>
#include <vector>
using namespace std;
using LL = long long;
const LL kMaxN = 505 * 505 + 5;
struct Node {
LL a, b;
};
LL dp[4][kMaxN], n, ans;
vector<Node> v[4], itm;
// 01 背包
void solve(LL dp[kMaxN], const vector<Node> &itm) {
for (auto i : itm) {
for (LL j = kMaxN - 1; j >= i.a; j--) {
dp[j] = max(dp[j], dp[j - i.a] + i.b);
}
}
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n;
for (LL i = 1, t, a, b; i <= n; i++) {
cin >> t >> a >> b;
v[t].push_back(Node{a, b});
}
for (LL i = 1; i <= 3; i++) {
itm.clear();
// 按照上述方式构建背包元素
for (auto j : v[i]) {
itm.push_back(Node{j.a + j.b, j.b});
}
solve(dp[i], itm);
}
for (LL i = 0; i < kMaxN; i++) { // 枚举 sum(a[i]+b[i]) 的最大值
LL sum = 0;
for (LL j = 1; j <= 3; j++) { // 枚举分组
sum += dp[j][i]; // 在 sum(a[i]+b[i]) 不超过最大值的情况下求 b[i] 最大和
}
ans = max(ans, sum - i); // 注意这里和式子一样要减最大值
}
cout << ans << '\n';
return 0;
}三元组
写了个 的暴力,光荣获得 分。
游戏
啥都没写。
连通性统计
啥都没写。
总结
总分:。
花了两个多小时写了 T1,然后 T2 没怎么想就只写了暴力,后两题根本没动。还是相较于上次有进步,至少成功地写出了 T1 捍卫了最后的尊严,但是后面去写美丽的 CCH 题单了就没做了,其实还可以骗得更多分数。T2 正解好像也不是很复杂,等会看看。