【题解】 AtCoder beginner Contest 153

ProblemA:Serval vs Monster

题目传送门: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+A1

代码:

#include 
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 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;
}

Problem C:Fennec vs Monster

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

解题思路:

题目给出 k k k个技能,可以把怪物血量直接变成0,那么我们对怪物数组从小到大排序,然后输出前 n − k n-k nk个的和(因为剩下的怪物只能平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 <

Problem D:Caracal vs Monster

题目传送门: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;
}

Problem E:Crested Ibis vs Monster

题目传送门: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[i1][j],dp[i][max(0,ja[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,ja[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;
}

Problem F:Silver Fox vs Monster

题目传送门: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 xd x + d x + d x+d区间内的所有怪物造成 A A A点伤害,怪物的血量小于等于0则死亡,问至少攻击多少次能消灭全部怪物。

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

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

#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;
}

你可能感兴趣的:(算法)