C - Quests
要解释第 i 个任务,前面的任务必须全部解锁。解锁后,剩余的次数可以重复做前面的任务。
先记录解锁到第 i 个任务的总经验值,记录前 i 个任务的最大值。
解锁到第 i 个,让剩余次数乘前面任务中b最大的。
如果 k > n k>n k>n 再算 m a x ( s u m , s a [ i ] + t ∗ m b [ i ] ) max(sum, sa[i] + t*mb[i]) max(sum,sa[i]+t∗mb[i]) 。
c++
#include<iostream>
#include<map>
#include<vector>
#include<string>
using namespace std;
void solve() {
int n, k;
cin >> n >> k;
vector<int>a(n), b(n);
vector<int>sa(n, 0), mb(n, 0);
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];
sa[0] = a[0];
for (int i = 1; i < n; i++) sa[i] = sa[i - 1] + a[i];
//前缀ai和
mb[0] = b[0];
for (int i = 1; i < n; i++) mb[i] = max(b[i], mb[i - 1]);
//前bi中最大的
int sum = 0;
for (int i = 0; i < min(n, k); i++) {
int t = k - 1 - i;
sum = max(sum, sa[i] + t*mb[i]);
}
if (k > n) {
sum = max(sum, sa[n - 1] + mb[n - 1] * (k - n));
}
cout << sum << endl;
}
int main() {
int T;
cin >> T;
while (T--) {
solve();
}
}
D - Three Activities
三个日子 x , y , z x,y,z x,y,z ,要 a [ x ] + b [ y ] + c [ z ] a[x]+b[y]+c[z] a[x]+b[y]+c[z] 最大。并且 x = = y ∣ ∣ x = = z ∣ ∣ y = = z x==y||x==z||y==z x==y∣∣x==z∣∣y==z 这样的日子是不行的。
先找出每个数组前三个最大的,记录元素的值和下标。再枚举所有组合 3 ∗ 3 = 27 3*3=27 3∗3=27 种。找最大值
c++
#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
#include <functional>
using namespace std;
typedef long long ll;
bool cmp(pair<int, int>a, pair<int, int>b) {
return a.first > b.first;
}
void solve() {
int n;
cin >> n;
vector<pair<int, int>>a, b, c;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
a.push_back({x, i});
}
for (int i = 0; i < n; i++) {
int x;
cin >> x;
b.push_back({x, i});
}
for (int i = 0; i < n; i++) {
int x;
cin >> x;
c.push_back({x, i});
}
sort(a.begin(),a.end(),cmp);
sort(b.begin(),b.end(),cmp);
sort(c.begin(),c.end(),cmp);
ll mx=0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
if(a[i].second==b[j].second||a[i].second==c[k].second||b[j].second==c[k].second) continue;
mx=max((ll)(a[i].first+b[j].first+c[k].first),mx);
}
}
}
cout<<mx<<endl;
}
int main() {
int T;
cin >> T;
while (T--) {
solve();
}
}
E - Game with Marbles (Easy )
两人轮流选,爱丽丝要最大化得分,鲍勃要最小化得分。用dp数组存每个状态的最优贡献。
mask 二进制位为1表示被选,0:未选的。
d p [ m a s k ] dp[mask] dp[mask] 表示选 mask 时,双方的最优策略。最终答案是 d p [ 0 ] dp[0] dp[0]
_ _ b u i l t i n _ p o p c o u n t ( m a s k ) \\ builtin\_popcount(mask) __builtin_popcount(mask) 统计 mask 中二进制1的个数。为偶数时爱丽丝选,奇数鲍勃选。
dp+逆序遍历+位掩码
为什么逆序遍历?
计算 d p [ m a s k ] dp[mask] dp[mask] 时,需要依赖 "选了一个新颜色后的状态" t 的 d p [ t ] dp[t] dp[t] 的值。
当 k = = n k == n k==n 时, m a s k = ( 1 < < n ) − 1 mask = (1<<n)-1 mask=(1<<n)−1(二进制全 1),没有可再选的颜色,游戏结束,贡献为 0。
m a s k & ( 1 < < j ) mask \& (1 << j) mask&(1<<j) 判断整数mask的第 j 个二进制位是否为1,看该颜色是否被选。非0->颜色已选 就跳过
m a s k ∣ ( 1 < < j ) mask | (1 << j) mask∣(1<<j) 选择颜色 j 将整数mask第 j 个二进制位设置为1其他位不变,来更新状态。实现状态转移
c++
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
int n;
cin >> n;
vector<ll>a(n), b(n);
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];
vector<ll>dp(1 << n, 0);
for (int mask = (1 << n) - 2; mask >= 0; mask--) {
int k = __builtin_popcount(mask);
if (k % 2 == 0) {//爱丽丝
dp[mask] = -1e18;
for (int j = 0; j < n; j++) {
if (mask & (1 << j)) continue;
int t = mask | (1 << j);
dp[mask] = max(dp[mask], a[j] - 1 + dp[t]);
}
} else {//鲍勃
dp[mask] = 1e18;
for (int j = 0; j < n; j++) {
if (mask & (1 << j)) continue;
int t = mask | (1 << j);
dp[mask] = min(dp[mask], 1 - b[j] + dp[t]);
}
}
}
cout << dp[0] << endl;
}
int main() {
int T;
cin >> T;
while (T--) {
solve();
}
}
F - Game with Marbles (Hard )
方法二:贪心
对于选择问题,谁先选哪个元素会影响总得分。
假设只有两种元素, x = ( a 1 , a 2 ) x=(a_1,a_2) x=(a1,a2) y = ( b 1 , b 2 ) y=(b_1,b_2) y=(b1,b2) ,爱丽丝先选有两种情况:
- 爱丽丝选 x ,鲍勃选 y : ( a 1 − 1 ) + ( 1 − b 2 ) = a 1 − b 2 (a_1-1)+(1-b_2)=a_1-b_2 (a1−1)+(1−b2)=a1−b2
- 爱丽丝选 y , 鲍勃选 x : ( a 2 − 1 ) + ( 1 − b 1 ) = a 2 − b 1 (a_2-1)+(1-b_1)=a_2-b_1 (a2−1)+(1−b1)=a2−b1
( a 1 − b 2 ) − ( a 2 − b 1 ) = ( a 1 + b 1 ) − ( a 2 + b 2 ) (a_1-b_2)-(a_2-b_1)=(a1 + b1) - (a2 + b2) (a1−b2)−(a2−b1)=(a1+b1)−(a2+b2)
如果a1 + b1 > a2 + b2:第一种顺序(先 x 后 y)总贡献更大,对爱丽丝更有利;
反之,第二种顺序更好。
所以对 a [ i ] + b [ i ] a[i]+b[i] a[i]+b[i] 排序,然后两人轮流选择。
c++
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool cmp(pair<ll, ll>x, pair<ll, ll>y) {
return x.first + x.second > y.first + y.second;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<ll> a(n), b(n);
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];
ll sum = 0;
vector<pair<ll, ll>>c;
for (int i = 0; i < n; i++) {
c.push_back({a[i], b[i]});
}
sort(c.begin(), c.end(), cmp);
for (int i = 0; i < n; i++) {
if (i % 2==0) {
sum += c[i].first - 1;
} else sum += 1 - c[i].second;
}
cout << sum << '\n';
}
return 0;
}