Codeforces Round 938 (Div. 3)H-The Most Reckless Defense (状压dp)

来源

题目

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𝑛×𝑚, on which k𝑘 towers are already placed and a path is laid out through which enemies will move. The cell at the intersection of the x𝑥-th row and the y𝑦-th column is denoted as (x,y)(𝑥,𝑦).

Each second, a tower deals pi𝑝𝑖 units of damage to all enemies within its range. For example, if an enemy is located at cell (x,y)(𝑥,𝑦) and a tower is at (xi,yi)(𝑥𝑖,𝑦𝑖) with a range of r𝑟, then the enemy will take damage of pi𝑝𝑖 if (x−xi)2+(y−yi)2≤r2(𝑥−𝑥𝑖)2+(𝑦−𝑦𝑖)2≤𝑟2.

Enemies move from cell (1,1)(1,1) to cell (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)(𝑛,𝑚) 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>0𝑟>0), in which case the health of all enemies will increase by 3r3𝑟. However, each r𝑟 can only be used for at most one tower.

Suppose an enemy has a base health of hℎ units. If the tower ranges are 22, 44, and 55, then the enemy's health at the start of the path will be h+32+34+35=h+9+81+243=h+333ℎ+32+34+35=ℎ+9+81+243=ℎ+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ℎ for which it is possible to set the ranges so that the player does not lose when an enemy with health hℎ passes through (without considering the additions for tower ranges).

Input

The first line contains an integer t𝑡 (1≤t≤1001≤𝑡≤100) — the number of test cases.

The first line of each test case contains three integers n𝑛, m𝑚, and k𝑘 (2≤n,m≤50,1≤k<n⋅m2≤𝑛,𝑚≤50,1≤𝑘<𝑛⋅𝑚) — the dimensions of the field and the number of towers on it.

The next n𝑛 lines each contain 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𝑘 lines — the description of the towers. Each line of description contains three integers xi𝑥𝑖, yi𝑦𝑖, and pi𝑝𝑖 (1≤xi≤n,1≤yi≤m,1≤pi≤5001≤𝑥𝑖≤𝑛,1≤𝑦𝑖≤𝑚,1≤𝑝𝑖≤500) — the coordinates of the tower and its attack parameter. All coordinates correspond to empty cells on the game field, and all pairs (xi,yi)(𝑥𝑖,𝑦𝑖) are pairwise distinct.

It is guaranteed that the sum of n⋅m𝑛⋅𝑚 does not exceed 25002500 for all test cases.

Output

For each test case, output the maximum amount of base health 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ℎ passes through (without considering the additions for tower ranges).

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

题意

有n*m的格子,“#”是敌人会经过的位置,有k个防御装置,有对应的位置和攻击力,对于每个防御装置,每增加r的攻击半径,怪物就会增加3^{r}点生命,问最多能防御多少点生命的怪物。

思路

       首先,可以发现r的取值不能很大,怪物的血量呈指数上升,稍微打一下表可以发现,r大于13的话就不可能杀死了。

       其次,一个防御建筑的有效攻击应该是,它能覆盖到的格子数*它的攻击力-它给怪物增加的血量。

       所以现在的问题就变成了,每个防御装置可以选择0-13半径的攻击范围,问最多能产生多少的有效攻击。因为半径的选择范围很小,可以用这一点进行状压dp

       首先可以先对每个防御塔,在不同的半径下能产生的有效伤害预处理。

bool check(int x,int y,int p,int q,int r){
	return (x-p)*(x-p)+(y-q)*(y-q)<=r*r;
}
 
for(int i=1;i<=k;i++){
    	int x,y,w;
    	cin>>x>>y>>w;
    	for(int j=0;j<=13;j++){
    		b[i][j]=0;
    		for(int p=1;p<=n;p++){
    			for(int q=1;q<=m;q++){
    				if(a[p][q]=='.')continue;
    				if(check(x,y,p,q,j))b[i][j]+=w;;
				}
			}
		}
	}

       定义dp[i][j]为前i个防御塔,用了j状态的半径(二进制的14位表示0-13是否使用),能产生的最多的有效攻击。

转移较为简单

for(int i=1;i<=k;i++){
		for(int j=0;j<(1<<14);j++){
			dp[i][j]=dp[i-1][j]+b[i][0];
			for(int p=1;p<=13;p++){
				if((j>>p)&1){
					dp[i][j]=max(dp[i][j],dp[i-1][j^(1<<p)]+b[i][p]);
				}
			}
		}
	}

最后比较每一种状态

#include <bits/stdc++.h>
using namespace std;
#define int long long
 
 
char a[55][55];
int dp[3000][1<<13];
int b[10000][20];
 
bool check(int x,int y,int p,int q,int r){
	return (x-p)*(x-p)+(y-q)*(y-q)<=r*r;
}
 
void solve(){
    int n,m,k;
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i]+1;
    }
    for(int i=1;i<=k;i++){
    	int x,y,w;
    	cin>>x>>y>>w;
    	for(int j=0;j<=13;j++){
    		b[i][j]=0;
    		for(int p=1;p<=n;p++){
    			for(int q=1;q<=m;q++){
    				if(a[p][q]=='.')continue;
    				if(check(x,y,p,q,j))b[i][j]+=w;;
				}
			}
		}
	}
	
	for(int i=1;i<=k;i++){
		for(int j=0;j<(1<<14);j++){
			dp[i][j]=dp[i-1][j]+b[i][0];
			for(int p=1;p<=13;p++){
				if((j>>p)&1){
					dp[i][j]=max(dp[i][j],dp[i-1][j^(1<<p)]+b[i][p]);
				}
			}
		}
	}
	int ans=0;
	for(int i=0;i<(1<<14);i++){
		int res=dp[k][i];
		int p=1;
		for(int j=1;j<=13;j++){
			p*=3;
			if((i>>j)&1)res-=p;
		}
		ans=max(ans,res);
	} 
	cout<<ans<<'\n';
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--)solve();
    return 0;
}

  • 32
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心刍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值