【题解】 AtCoder beginner Contest 153

ProblemA:Serval vs Monster

题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_a

解题思路:

问你需要多少次技能才能消灭怪兽。

输出 H A ⌈\frac{H}{A}⌉ 即可,可以转化为 H + A 1 A \lfloor \frac {H+A-1} A \rfloor

代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int h, a;
    cin >> h >> a;
    cout << ceil(h * 1.0 / a) << endl;
    return 0;
}

ProblemB:Common Raccoon vs Monster

题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_b

解题思路:

给你 H H N N N N 个数组成的技能数组 A A ,每个技能只能使用过一遍,问你能不能把怪兽的 H H 减到0以下。

#include <bits/stdc++.h>
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;
}

Problem C:Fennec vs Monster

题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_c

解题思路:

题目给出 k k 个技能,可以把怪物血量直接变成0,那么我们对怪物数组从小到大排序,然后输出前 n k n-k 个的和(因为剩下的怪物只能平A了)。

代码:

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, k, t;
    vector<int> 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 <<endl;
    return 0;
}

Problem D:Caracal vs Monster

题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_d

解题思路:

我们可以将场上所有的怪兽攻击一次计算为一次攻击,推导可得:

为了体力战胜1只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.
代码:

#include <bits/stdc++.h>
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;
}

Problem E:Crested Ibis vs Monster

题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_e

解题思路:

这道题是典型的背包dp模板题,就是完全背包条件下,找大于等于最大容量的最小价值。

定义 d p [ i ] [ j ] dp[i][j] 为前 i i 个咒语造成了 j j 点伤害最少使用的 M P 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])

时间复杂度: O ( M N ) O(MN)

#include <bits/stdc++.h>
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])

即选择下一个技能的话就要加上下一个技能的b[ i ],不选择的话就不加。

#include <bits/stdc++.h>
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;
}

Problem F:Silver Fox vs Monster

题目传送门:https://atcoder.jp/contests/abc153/tasks/abc153_f

解题思路:

n n 个怪物,每个怪物的坐标为 x i x_i ,血量为 h i h_i ,在 x x 处施展一次攻击会对 x d x - d x + d x + d 区间内的所有怪物造成 A A 点伤害,怪物的血量小于等于0则死亡,问至少攻击多少次能消灭全部怪物。

我们首先对怪物按坐标进行排序。首先,攻击顺序肯定是不重要的,而且最左边的点如果要被消灭的话一定会被攻击 H i + A 1 A \lfloor \frac {H_i+A-1} A \rfloor 次,把攻击的血量累计到总伤害,然后后面 2 d 2*d 的距离都会受到影响,我们需要知道最近不受伤害的点,这个可以使用双指针算法,也可以使用二分,然后运用差分的思想,构建一个差分数组,把这个坐标的值加扣的血量,每次到一个点我们的总伤害先减去这个差分数组,这样就能维护当前被影响的血量。

这里还可以使用线段树或者树状数组来修改区间,可以当做练手来试试。

#include <bits/stdc++.h>
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;
}
发布了128 篇原创文章 · 获赞 20 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/u011544909/article/details/104212710