题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_a
解题思路:
问你需要多少次技能才能消灭怪兽。
输出 ⌈ H A ⌉ ⌈\frac{H}{A}⌉ ⌈AH⌉即可,可以转化为 ⌊ H + A − 1 A ⌋ \lfloor \frac {H+A-1} A \rfloor ⌊AH+A−1⌋
代码:
#include
using namespace std;
int main()
{
int h, a;
cin >> h >> a;
cout << ceil(h * 1.0 / a) << endl;
return 0;
}
题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_b
解题思路:
给你 H H H和 N N N, N N N个数组成的技能数组 A A A,每个技能只能使用过一遍,问你能不能把怪兽的 H H H减到0以下。
#include
using namespace std;
int main() {
int n, t, a;
cin >> n >> a;
int tot = 0;
for(int i = 0; i < a; ++i) {
cin >> t;
tot += t;
}
if(tot >= n) puts("Yes");
else puts("No");
return 0;
}
题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_c
解题思路:
题目给出 k k k个技能,可以把怪物血量直接变成0,那么我们对怪物数组从小到大排序,然后输出前 n − k n-k n−k个的和(因为剩下的怪物只能平A了)。
代码:
#include
using namespace std;
int main() {
int n, k, t;
vector health;
cin >> n >> k;
for(int i = 0; i < n; ++i) {
cin >> t;
health.push_back(t);
}
sort(begin(health), end(health));
if(k > n) k = n;
long long tot = accumulate(begin(health), begin(health) + n - k, (long long)0);
cout << tot <
题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_d
解题思路:
我们可以将场上所有的怪兽攻击一次计算为一次攻击,推导可得:
为了体力战胜1只H怪兽所需的攻击次数为 f ( H ) f(H) f(H):
f ( H ) = { 2 × f ( ⌊ ( H / 2 ) ⌋ ) ( H > 1 ) 1 ( H = 1 ) f(H) = \left\{\begin{matrix} 2\times f(\left \lfloor (H/2) \right \rfloor) \ \ (H>1)\\ 1 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (H=1) \end{matrix}\right. f(H)={2×f(⌊(H/2)⌋) (H>1)1 (H=1)
代码:
#include
using namespace std;
typedef long long ll;
int main() {
ll n;
ll now = 1;
ll tot = 0;
cin >> n;
while(n >= 1) {
tot += now;
now <<= 1;
n /= 2;
}
cout << tot << endl;
return 0;
}
题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_e
解题思路:
这道题是典型的背包dp模板题,就是完全背包条件下,找大于等于最大容量的最小价值。
定义 d p [ i ] [ j ] dp[i][j] dp[i][j]为前 i i i个咒语造成了 j j j点伤害最少使用的 M P MP MP量,那么状态转移方程为:
d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j ] , d p [ i ] [ m a x ( 0 , j − a [ i ] ) ] + b [ i ] ) dp[i][j] = min(dp[i-1][j], dp[i][max(0,j-a[i])] +b[i]) dp[i][j]=min(dp[i−1][j],dp[i][max(0,j−a[i])]+b[i])
时间复杂度: O ( M N ) O(MN) O(MN)
#include
using namespace std;
typedef long long ll;
const int N = 1e3 + 5;
const int M = 2e4 + 5;
int a[N], b[N];
int dp[N][M];
int main()
{
int h, n;
cin >> h >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i] >> b[i];
memset(dp, 0x3f, sizeof dp);
dp[0][0] = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 0; j <= h; ++j) {
dp[i][j] = min(dp[i - 1][j], dp[i][max(0, j - a[i])] + b[i]);
}
}
cout << dp[n][h] << endl;
return 0;
}
我们发现,第一维的空间完全可以省略,所以可以简化成如下代码:
d p [ j ] = m i n ( d p [ j ] , d p [ m a x ( 0 , j − a [ i ] ) ] + b [ i ] ) dp[j] = min(dp[j], dp[max(0,j-a[i])] +b[i]) dp[j]=min(dp[j],dp[max(0,j−a[i])]+b[i])
即选择下一个技能的话就要加上下一个技能的b[ i ],不选择的话就不加。
#include
using namespace std;
typedef long long ll;
const int N = 1e3 + 5;
const int M = 2e4 + 5;
int a[N], b[N];
int dp[N];
int main()
{
int h, n;
cin >> h >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i] >> b[i];
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 0; j <= h; ++j) {
dp[j] = min(dp[j], dp[max(0, j - a[i])] + b[i]);
}
}
cout << dp[h] << endl;
return 0;
}
题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_f
解题思路:
有 n n n个怪物,每个怪物的坐标为 x i x_i xi,血量为 h i h_i hi,在 x x x处施展一次攻击会对 x − d x - d x−d到 x + d x + d x+d区间内的所有怪物造成 A A A点伤害,怪物的血量小于等于0则死亡,问至少攻击多少次能消灭全部怪物。
我们首先对怪物按坐标进行排序。首先,攻击顺序肯定是不重要的,而且最左边的点如果要被消灭的话一定会被攻击 ⌊ H i + A − 1 A ⌋ \lfloor \frac {H_i+A-1} A \rfloor ⌊AHi+A−1⌋次,把攻击的血量累计到总伤害,然后后面 2 ∗ d 2*d 2∗d的距离都会受到影响,我们需要知道最近不受伤害的点,这个可以使用双指针算法,也可以使用二分,然后运用差分的思想,构建一个差分数组,把这个坐标的值加扣的血量,每次到一个点我们的总伤害先减去这个差分数组,这样就能维护当前被影响的血量。
这里还可以使用线段树或者树状数组来修改区间,可以当做练手来试试。
#include
using namespace std;
typedef long long ll;
const int N = 2e5 + 50; // 蜜汁RE,不知道为什么,2e5+10就RE
ll c[N];
struct Node {
int x, h;
inline bool operator < (const Node& t) const {
return x < t.x;
}
} p[N];
int main()
{
int n, a, d;
cin >> n >> d >> a;
for(int i = 1; i <= n; ++i)
cin >> p[i].x >> p[i].h;
sort(p + 1, p + n + 1);
ll ans = 0;
// 双指针
for(int i = 1, j = 1; i <= n; ++i) {
while(j <= n && p[j].x <= p[i].x + 2 * d) ++j;
ll need = max((p[i].h - c[i] * a + a - 1) / a, 0ll);
ans += need;
// 构造差分序列
c[i] += need;
c[j] -= need;
c[i + 1] += c[i];
}
cout << ans << endl;
return 0;
}