【Codeforces】Educational Codeforces Round 137 - E. FTL

234 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 1 月更文挑战」的第19天,点击查看活动详情

【Codeforces】Educational Codeforces Round 137 - E. FTL

题目链接

Problem - E - Codeforces

题目大意

Monocarp 在他的飞船上安装了两台激光炮。激光炮 1 经过 t1t_1 的时间充能后可以发射,威力为 p1p_1;激光炮 2 经过 t2t_2 的时间充能后可以发射,威力为 p2p_2
当一台激光炮充能完毕后,Monocarp 可以发射它,也可以等待另一台激光炮充能完毕后同时发射两台激光炮。

敌方飞船具有 hh 耐久度和 ss 护盾。当 Monocarp 仅发射激光炮 ii 时,敌方飞船会受到 (pis)(p_i−s) 点伤害。(即它的耐久度减少 (pis)(p_i−s))。如果同时发射两台激光炮,敌方飞船会受到 (p1+p2s)(p_1+p_2−s) 点伤害。敌方飞船在其耐久度变为 0 或更低时被视为已摧毁。

最初,两个激光炮充能进度为 0。

Monocarp 摧毁敌方飞船所需的最短时间是多少?

1s<p1,p25000,1h5000,1t1,t210121\le s<p_1,p_2\le 5000,1\le h\le 5000,1\le t_1,t_2\le 10^{12}

思路

dp[p]dp[p] 表示造成 pp 点伤害需要的最短时间。

先考虑恰好发射了 ii 枚激光炮 1 造成的伤害,我们花费的时间 t=it1t=i*t_1

  • 如果只单独发射两种激光炮,此时可以发射 tt2\frac{t}{t_2} 枚激光炮 2。造成的伤害 p=i(p1s)+tt2(p2s)p=i*(p_1-s)+\frac{t}{t_2}*(p_2-s)
  • 我们显然可以暂时不发送第 tt2\frac{t}{t_2}枚激光炮,等到第 ii 枚激光炮充能完毕后将二者同时发送,使得造成的伤害增加 ss
  • tt 更新造成不超过 pp 点伤害所需要的时间。

同样的,我们可以处理出恰好发射了 ii 枚激光炮 2 造成的伤害,并更新 dp 数组。

但是我们发现使用上述方法更新出的 dp 数组仅包含只使用了一次组合攻击的信息。该怎样求出使用了若干次组合攻击造成 pp 点伤害需要的最短时间呢?

容易发现,当我们同时发射了两种激光炮,两个激光炮的充能进度都会归 0,与第 0 时刻的情况相同。所以我们可以用造成 kk 点伤害需要的时间和造成 iki-k 点伤害需要的时间之和来更新 造成 ii 点伤害需要的世界。即 dp[i]=min1ki{dp[k]+dp[ik]}dp[i]=min_{1\le k\le i}\{dp[k]+dp[i-k]\}

代码

#include <bits/stdc++.h>
using namespace std;
using LL=long long;
LL t1,t2,dp[5001],t,p1,p2,s,h,p;
int solve()
{
	cin>>p1>>t1;
	cin>>p2>>t2;
	cin>>h>>s;
	dp[0]=0;
	for (int i=1;i<=h;++i) dp[i]=1ll<<60;
	for (int i=1;;++i)
	{
		t=i*t1;
		p=i*p1-i*s+t/t2*p2-t/t2*s;
		if (t/t2) p+=s;
		for (int j=min(p,h);dp[j]>t;--j) dp[j]=t;
		if (p>h) break;
	}
	for (int i=1;;++i)
	{
		t=i*t2;
		p=i*p2-i*s+t/t1*p1-t/t1*s;
		if (t/t1) p+=s;
		for (int j=min(p,h);dp[j]>t;--j) dp[j]=t;
		if (p>h) break;
	}
	for (int i=1;i<=h;++i)
		for (int k=1;k<=i-k;++k) dp[i]=min(dp[i],dp[k]+dp[i-k]);
	
	printf("%lld\n",dp[h]);
	return 0;
}
int main()
{
	solve();
	return 0;
}