第 14 届陕西省国际大学生程序设计竞赛 M题思路分享(贪心)
题意概述
给定一个 \(n\times m\) 的网格,# 为障碍,. 为空地,可以在空地上选择建造两种防御塔(可以不建),防御塔 \(A\) 的基础 \(dps\) 为 \(d_a\),防御塔 \(B\) 的基础 \(dps\) 为 \(d_b\),要求最大化总 \(dps\)。
防御塔 \(A\) 周围每有一个同类防御塔,它的 \(dps\) 增大 \(1\);防御塔 \(B\) 周围每有一个同类防御塔,它的 \(dps\) 减小 \(1\)。
输出最大 \(dps\) 和方案。
\(1\le n,m \le 2000\)。
思路
首先不可能不建,因为放 \(A\) 一定更优。
考虑全部建 \(A\),然后讨论把某个 \(A\) 替换成 \(B\)。
如果某个相邻防御塔为 \(A\),把当前 \(A\) 换成 \(B\),原本一对相邻 \(A\) 被拆掉,贡献减少 \(2\);如果某个相邻防御塔为 \(B\),把当前 \(A\) 换成 \(B\),形成一对相邻 \(B\),贡献减少 \(2\)。
记某个格子周围空地的数量为 \(cnt\),也就是说,只要把 \(A\) 换成 \(B\),贡献一定会减少 \(2\cdot cnt\)。因此只要 \(d_b-d_a-2\cdot cnt \gt 0\),就建 \(B\),否则建 \(A\)。
时间复杂度 \(\mathcal{O}(n\cdot m)\)。
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const vector<pair<int,int>> dir = {{-1,0},{0,1},{1,0},{0,-1}};
void solve(){
int n,m;
cin >> n >> m;
ll d1,d2;
cin >> d1 >> d2;
vector<string> s(n+1);
for (int i=1;i<=n;i++){
cin >> s[i];
s[i] = ' '+s[i];
}
auto pd = [&](int x,int y){
return x>=1 && x<=n && y>=1 && y<=m;
};
auto b = s;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
if (s[i][j]=='#') continue;
int cnt = 0;
for (auto& [dx,dy]:dir){
int nx = i+dx;
int ny = j+dy;
if (pd(nx,ny) && s[nx][ny]!='#'){
cnt++;
}
}
if (d2-d1-cnt*2>0){
b[i][j] = 'B';
}
else{
b[i][j] = 'A';
}
}
}
ll res = 0;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
if (b[i][j]=='#') continue;
res += b[i][j]=='A'?d1:d2;
for (auto& [dx,dy]:dir){
int nx = i+dx;
int ny = j+dy;
if (pd(nx,ny)){
if (b[nx][ny]=='A' && b[i][j]=='A'){
res++;
}
else if (b[nx][ny]=='B' && b[i][j]=='B'){
res--;
}
}
}
}
}
cout << res << '\n';
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
cout << b[i][j];
}
cout << '\n';
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号