给敌人加血可以看成是减少防御塔的攻击力,那么一个塔对敌人能造成的最大伤害即为 \(500\pi r^2-3^r\),注意到 \(r=12\) 时已经小于 \(0\) 了,所以半径不会很大,又因为每个 \(r\) 只能选一次,所以有效的塔很少,考虑状压 \(dp\)。
具体地,我们设 \(f_{i,S}\) 表示前 \(i\) 个塔中,被选到的塔的半径集合为 \(S\),所造成的伤害(攻击力)为多少。显然最后答案就等于最大的伤害。有转移方程:
\[f_{i,S}=\max(f_{i-1,S},f_{i-1,S\oplus r}+p_i\times calc(i,r)-3^r) \]含义很简单,就是当前选或不选。\(calc(i,r)\) 表示第 \(i\) 座塔半径为 \(r\) 时能攻击到几次敌人。预处理 \(calc\) 的复杂度为 \(\mathcal{O}(k\cdot R^3)\),\(dp\) 的复杂度为 \(\mathcal{O}(k\cdot R\cdot 2^R)\),总复杂度 \(\mathcal{O}(k\cdot R\cdot (R^2+2^R))\)。
其实也可以最后一起减去 \(\sum 3^r\) 的,两种都对。一开始我不理解为啥两种写法是等价的,想了想,大概是因为每次取 \(\max\) 的两项只差一个 \(r\),所以要么都减了,要么都没减,不影响相对大小,也就不影响结果。
其实这题还能费用流。每个塔作为左部点,\(12\) 个半径作为右部点,边权或者说费用就是 \(p_i\times calc(i,r)-3^r\),跑一遍二分图带权最大匹配,也就是最大费用最大流即可。不过我没写,听 @cuiyulin 口胡的。
\(dp\) 代码:
#include<bits/stdc++.h>
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define mp(a,b) make_pair(a,b)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define fi first
#define se second
#define eb emplace_back
#define ep emplace
using namespace std;
inline int read();
typedef long long ll;
typedef double db;
const int N=50+5;
const int inf=0x3f3f3f3f;
const int INF=0x7f7f7f7f;
const int R=12;
int sqr(int x){return (x)*(x);}
int n,m,k;
char s[N][N];
struct tower{
int x,y,p;
}a[N*N];
int f[N*N][1<<R];
int calc[N*N][R+1];
int Calc(int x,int y,int r){
int res=0;
FOR(i,max(x-r,1),min(n,x+r))
FOR(j,max(y-r,1),min(m,y+r))
if(s[i][j]=='#'&&sqr(x-i)+sqr(y-j)<=sqr(r))
res++;
return res;
}
void work(){
n=read(),m=read(),k=read();
FOR(i,1,n) scanf("%s",s[i]+1);
FOR(i,1,k) a[i]={read(),read(),read()};
FOR(i,1,k){
int x=a[i].x,y=a[i].y;
FOR(r,1,R) calc[i][r]=Calc(x,y,r);
}
memset(f,-0x3f,sizeof f);
f[0][0]=0;
FOR(i,1,k){
for(int S=0;S<(1<<R);++S){
f[i][S]=f[i-1][S];
FOR(r,1,R)
if(S>>(r-1)&1)
f[i][S]=max(f[i][S],f[i-1][S^(1<<r-1)]+a[i].p*calc[i][r]-(int)pow(3,r));
}
}
int ans=0;
FOR(i,1,k)
FOR(S,0,(1<<R)-1)
ans=max(ans,f[i][S]);
printf("%d\n",ans);
}
int main()
{
int T=read();
while(T--) work();
return 0;
//あまりに短い夏だけで何を残していけるのかな?
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return f*x;
}
标签:ch,const,int,cdot,Most,Defense,calc,Reckless,define
From: https://www.cnblogs.com/LHLeisus/p/18141079