第 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;
}
posted @ 2026-05-18 14:32  kzssCCC  阅读(20)  评论(0)    收藏  举报