Codeforces Round 938 Div.3 H. The Most Reckless Defense 题解 状压dp

The Most Reckless Defense

题目描述

You are playing a very popular Tower Defense game called “Runnerfield 2”. In this game, the player sets up defensive towers that attack enemies moving from a certain starting point to the player’s base.

You are given a grid of size n × m n \times m n×m, on which k k k towers are already placed and a path is laid out through which enemies will move. The cell at the intersection of the x x x-th row and the y y y-th column is denoted as ( x , y ) (x, y) (x,y).

Each second, a tower deals p i p_i pi units of damage to all enemies within its range. For example, if an enemy is located at cell ( x , y ) (x, y) (x,y) and a tower is at ( x i , y i ) (x_i, y_i) (xi,yi) with a range of r r r, then the enemy will take damage of p i p_i pi if ( x − x i ) 2 + ( y − y i ) 2 ≤ r 2 (x - x_i) ^ 2 + (y - y_i) ^ 2 \le r ^ 2 (xxi)2+(yyi)2r2.

Enemies move from cell ( 1 , 1 ) (1, 1) (1,1) to cell ( n , m ) (n, m) (n,m), visiting each cell of the path exactly once. An enemy instantly moves to an adjacent cell horizontally or vertically, but before doing so, it spends one second in the current cell. If its health becomes zero or less during this second, the enemy can no longer move. The player loses if an enemy reaches cell ( n , m ) (n, m) (n,m) and can make one more move.

By default, all towers have a zero range, but the player can set a tower’s range to an integer r r r ( r > 0 r>0 r>0), in which case the health of all enemies will increase by 3 r 3^r 3r. However, each r r r can only be used for at most one tower.

Suppose an enemy has a base health of h h h units. If the tower ranges are 2 2 2, 4 4 4, and 5 5 5, then the enemy’s health at the start of the path will be h + 3 2 + 3 4 + 3 5 = h + 9 + 81 + 243 = h + 333 h + 3 ^ 2 + 3 ^ 4 + 3 ^ 5 = h + 9 + 81 + 243 = h + 333 h+32+34+35=h+9+81+243=h+333. The choice of ranges is made once before the appearance of enemies and cannot be changed after the game starts.

Find the maximum amount of base health h h h for which it is possible to set the ranges so that the player does not lose when an enemy with health h h h passes through (without considering the additions for tower ranges).

输入格式

The first line contains an integer t t t ( 1 ≤ t ≤ 100 1 \le t \le 100 1t100) — the number of test cases.

The first line of each test case contains three integers n n n, m m m, and k k k ( 2 ≤ n , m ≤ 50 , 1 ≤ k < n ∗ m 2 ≤ n, m ≤ 50, 1 ≤ k < n * m 2n,m50,1knm) — the dimensions of the field and the number of towers on it.

The next n n n lines each contain m m m characters — the description of each row of the field, where the character “.” denotes an empty cell, and the character “#” denotes a path cell that the enemies will pass through.

Then follow k k k lines — the description of the towers. Each line of description contains three integers x i x_i xi, y i y_i yi, and p i p_i pi ( 1 ≤ x i ≤ n , 1 ≤ y i ≤ m , 1 ≤ p i ≤ 500 1 \le x_i \le n, 1 \le y_i \le m, 1 \le p_i \le 500 1xin,1yim,1pi500) — the coordinates of the tower and its attack parameter. All coordinates correspond to empty cells on the game field, and all pairs ( x i , y i ) (x_i, y_i) (xi,yi) are pairwise distinct.

It is guaranteed that the sum of n ⋅ m n \cdot m nm does not exceed 2500 2500 2500 for all test cases.

输出格式

For each test case, output the maximum amount of base health h h h on a separate line, for which it is possible to set the ranges so that the player does not lose when an enemy with health h h h passes through (without considering the additions for tower ranges).

If it is impossible to choose ranges even for an enemy with 1 1 1 unit of base health, output “0”.

样例 #1

样例输入 #1

6
2 2 1
#.
##
1 2 1
2 2 1
#.
##
1 2 2
2 2 1
#.
##
1 2 500
3 3 2
#..
##.
.##
1 2 4
3 1 3
3 5 2
#.###
#.#.#
###.#
2 2 2
2 4 2
5 5 4
#....
#....
#....
#....
#####
3 2 142
4 5 9
2 5 79
1 3 50

样例输出 #1

0
1
1491
11
8
1797

提示

In the first example, there is no point in increasing the tower range, as it will not be able to deal enough damage to the monster even with 1 1 1 unit of health.

In the second example, the tower has a range of 1 1 1, and it deals damage to the monster in cells ( 1 , 1 ) (1, 1) (1,1) and ( 2 , 2 ) (2, 2) (2,2).

In the third example, the tower has a range of 2 2 2, and it deals damage to the monster in all path cells. If the enemy’s base health is 1491 1491 1491, then after the addition for the tower range, its health will be 1491 + 3 2 = 1500 1491 + 3 ^ 2 = 1500 1491+32=1500, which exactly equals the damage the tower will deal to it in three seconds.

In the fourth example, the tower at ( 1 , 2 ) (1, 2) (1,2) has a range of 1 1 1, and the tower at ( 3 , 1 ) (3, 1) (3,1) has a range of 2 2 2.

原题

CF——传送门
洛谷——传送门

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
// 若R大于12,500⋅π⋅r^2−3^r<0 那么塔楼对敌人的血量削减为负贡献,所以R最大即为12
// 由于R很小,所以采用状压dp枚举半径分布的子集
 
void solve()
{
    int n, m, k;
    cin >> n >> m >> k;
    vector<vector<char>> ground(n + 1, vector<char>(m + 1)); // 场地
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> ground[i][j];
        }
    }
    vector<int> tower_x(k); // 塔楼的纵坐标
    vector<int> tower_y(k); // 塔楼的横坐标
    vector<int> p(k);       // 塔楼的攻击参数
    for (int i = 0; i < k; i++)
    {
        cin >> tower_x[i] >> tower_y[i] >> p[i];
    }
    vector<vector<int>> cover(k, vector<int>(13)); // 第i+1座塔楼半径为j时覆盖的路径点的数量
    for (int i = 0; i < k; i++)
    {
        for (int j = 1; j <= 12; j++)
        {
            int cnt = 0; // 覆盖的路径点的数量
            int x = tower_x[i];
            int y = tower_y[i];
            for (int nx = x - j; nx <= x + j; nx++) // 枚举以塔楼为中心点,2*j+1为边长的正方形
            {
                for (int ny = y - j; ny <= y + j; ny++)
                {
                    if (nx >= 1 && nx <= n && ny >= 1 && ny <= m)         // 不越界
                        if (pow(x - nx, 2) + pow(y - ny, 2) <= pow(j, 2)) // 在圆内
                            if (ground[nx][ny] == '#')                    // 是路径点
                                cnt++;
                }
            }
            cover[i][j] = cnt;
        }
    }
    // 由于每个r最多对应一个塔楼,所以最多为12个塔楼设置攻击范围
    int num = 12;
    vector<vector<int>> dp(k + 1, vector<int>((1 << num), -1e9)); // dp[i][j]表示设置前i个塔楼,半径状态压缩为j时塔楼可造成的最大伤害
    dp[0][0] = 0;
    for (int i = 1; i <= k; i++)
        for (int s = 0; s < (1 << num); s++) // s为上一行的dp状态
        {
            dp[i][s] = max(dp[i][s], dp[i - 1][s]);
            for (int j = 0; j < num; j++)
            {
                int s2 = s | (1 << j); // 状态压缩加上该塔楼该半径
                if (s2 != s)
                {
                    dp[i][s2] = max(dp[i][s2], dp[i - 1][s] + cover[i - 1][j + 1] * p[i - 1]);
                }
            }
        }
    int ans = 0;
    for (int s = 0; s < (1 << num); s++)
    {
        int ex = 0;   // 额外增加的敌人生命值
        int ppow = 3; // 3的r次方
        for (int j = 0; j < num; j++)
        {
            if ((s >> j) & 1)
            {
                ex += ppow;
            }
            ppow *= 3;
        }
        for (int j = 0; j <= k; j++)
        {
            ans = max(ans, dp[j][s] - ex);
        }
    }
    cout << ans << '\n';
 
    return;
}
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
 
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
 
    return 0;
}
  • 36
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值