传送门:1743E
标签:动态规划
题目大意
Monocarp 正在玩一款电子游戏。在游戏中,他控制一艘宇宙飞船并需要摧毁一艘敌方宇宙飞船。
Monocarp 的宇宙飞船上安装了两台激光器。这两台激光器 1 和 2 都有两个值:
- ( p_i ) —— 激光器的功率;
- ( t_i ) —— 激光器的重装时间。
当激光器完全充电时,Monocarp 可以选择射击或等待另一台激光器充电,然后同时射击两台激光器。敌方宇宙飞船有 ( h ) 的耐久度和 ( s ) 的护盾容量。当 Monocarp 射击敌方宇宙飞船时,它会受到 ( (P - s) ) 的伤害(即 ( (P - s) ) 从其耐久度中减去),其中 ( P ) 是 Monocarp 射击的激光器的总功率(即 ( p_i ) 如果他只射击激光器 ( i ) 和 ( p_1 + p_2 ) 如果他同时射击两台激光器)。当敌方宇宙飞船的耐久度变为 0 或更低时,它被认为被摧毁。最初,两台激光器都没有充电。Monocarp 摧毁敌方宇宙飞船所需的最短时间是多少?
输入:第一行包含两个整数,表示第一台激光器的功率和重装时间。第二行包含两个整数,表示第二台激光器的功率和重装时间。第三行包含两个整数,表示敌方宇宙飞船的耐久度和护盾容量。最后一个约束意味着 Monocarp 总能摧毁敌方宇宙飞船。
输出:输出一个整数——Monocarp 摧毁敌方宇宙飞船所需的最短时间。
算法分析
- 在任何时候,我们有三种可能的选择:等待并发射第一束激光、第二束激光和两束激光。有时等待两束激光是有意义的,因为可以造成比单独发射两束激光更多的伤害。第一个结论:贪婪算法不起作用。也许有一种足够聪明的贪婪算法,但很难想到。第二个结论:暴力算法不起作用。它实际上在约束条件为2000时起作用,但数据范围到5000就没用了。
- 因此,我们可以使用动态规划。由于所有时间都非常大,我们希望避免将它们作为状态。然而,较小的是敌舰的耐久度和我们需要摧毁它的射击次数。理想情况下,我们希望有一些 ( dp[i] ) ——对敌舰造成 ( i ) 点伤害所需的最短时间。这样,( dp[n] ) 将是答案。遗憾的是,如何完全消除重装时间并不立即清楚。可能有不同时间的重装状态,直到与相同伤害处理的重装时间相同,我们不知道应该保留哪些。
- 然后我们可以让动态规划状态更复杂。让 ( dp[i] ) 是如果最后一击是同时从两束激光发射的,造成 ( i ) 点伤害所需的最短时间。这样我们就知道两束激光的重装时间——它们是满的 ( t_1 ) 和 ( t_2 )。( dp[0] = 0 ),因为时刻0两束激光都未充电,就像射击后一样。转换是什么?好吧,现在我们必须多次发射每束激光,然后等待直到两者都充电并同时发射。两束激光现在可以被认为是相互独立的。
- 让前一个双击和下一个双击之间的时间为某个值 ( t )。在这段时间内,等待直到发射每束激光都没有意义。所以我们等待了 ( t_1 ),发射了第一束激光,又等待了 ( t_1 ),再次发射,直到我们不能再发射,因为激光在双击之前没有时间重新充电。第二束激光也是如此。很显然,只有 ( t ) 是 ( t_1 ) 或 ( t_2 ) 的倍数才是最优的。
- 在摧毁敌舰之前,最后一击可能不是双击。然而,它仍然遵循相同的想法。这意味着每束激光都在不停地射击,直到敌舰被摧毁。因此,摧毁时间仍然是重装时间的倍数。总体时间复杂度为O(h2)。
代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5005;
int p1,p2,h,s;
ll t1,t2;
ll f[N];
int main(){
cin>>p1>>t1>>p2>>t2>>h>>s;
if(t1>t2)swap(t1,t2),swap(p1,p2);
for(int i=1;i<=h;i++)f[i]=1e18;
ll ans=1e18;
for(int i=0;i<h;i++){
if(f[i]==1e18)continue;
for(int j=1;j<=h;j++){
ll x=j,y=(j*t1)/t2;
ll k=1ll*x*p1+1ll*y*p2;
k-=1ll*(x+y-(y>0))*s;
ll val=f[i]+j*t1;
if(i+k<h)f[i+k]=min(f[i+k],val);
else ans=min(ans,val);
}
swap(t1,t2),swap(p1,p2);
for(int j=1;j<=h;j++){
ll x=j,y=(j*t1)/t2;
ll k=1ll*x*p1+1ll*y*p2;
k-=1ll*(x+y-(y>0))*s;
ll val=f[i]+j*t1;
if(i+k<h)f[i+k]=min(f[i+k],val);
else ans=min(ans,val);
}
}
cout<<ans<<'\n';
return 0;
}