CF1396C Monster Invaders
自己做出来的蓝题 dp,喜。
容易得到如下结论:
1、每关内部比较独立。
2、小关没杀完就走一定不优。
3、经过计算可得,杀一个关卡只有三种方式:(前者为杀小怪的方式,后者为杀 boss 的方式,计算式里面同理)
方式一:手枪 + AWP \(a_i \times r_1 + r_3\)。
方式二:手枪 + 手枪 \(a_i \times r_1 + 2 \times r_1\),另外还有 \(2 \times d\) 的移动代价。
方式三:激光枪 + 手枪 \(r_2 + r_1\),另外还有 \(2 \times d\) 的移动代价。
4、我们记结论 \(3\) 中采用方式二、三的关卡为回头点,可以证明在 \(i + 1\) 处返回回头点 \(i\),这样全部通关所产生的额外移动代价最小。
根据结论 \(1\),很容易想到本题可以使用 dp。
根据结论 \(3,4\),发现该转移存在两种特殊情况:
1、对于连续多个回头点,可以两两匹配减少移动代价。因此转移要分奇偶。
2、当第 \(n - 1\) 关是一个单独的回头点,且第 \(n\) 关不是回头点时,移动代价只有 \(d\)。
我们记 \(f_{i, j, k}\) 表示前 \(i\) 关,第 \(i\) 关不是 / 是(\(j = 0 / 1\))回头点,若是,第 \(i\) 关是连续的第偶数 / 奇数(\(k = 0/1\))个回头点。转移方程显然。
时间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l); i <= (r); ++ i)
#define G(i,r,l) for(int i(r); i >= (l); -- i)
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int N = 1500000;
const ll inf = 1e18;
ull n, r1, r2, r3, d;
int a[N];
ull f[N][2][2], ans = inf;
signed main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> r1 >> r2 >> r3 >> d;
F(i, 1, n) cin >> a[i];
f[1][0][0] = a[1] * r1 + r3;
f[1][1][1] = min((a[1] + 2) * r1, r2 + r1) + 2 * d;
f[1][1][0] = inf;
F(i, 2, n){
f[i][0][0] = min({f[i - 1][0][0], f[i - 1][1][1], f[i - 1][1][0]}) + a[i] * r1 + r3;
f[i][1][1] = min(f[i - 1][1][0], f[i - 1][0][0]) + min(r2 + r1, (a[i] + 2) * r1) + 2 * d;
f[i][1][0] = f[i - 1][1][1]+ min(r2 + r1, (a[i] + 2) * r1);
}
f[n][0][0] = min(f[n - 1][1][1] + a[n] * r1 + r3 - d, f[n][0][0]);
// F(i, 1, n){
// printf("%llu\t%llu\t%llu\t%llu\t%llu\n", f[i][0][0], f[i][1][0], f[i][1][1], f[i][2][0], f[i][2][1]);
// }
cout << min({f[n][0][0], f[n][1][0], f[n][1][1]}) + (n - 1) * d << '\n';
return fflush(0), 0;
}

浙公网安备 33010602011771号