无线网络信号塔覆盖

题意

给一个 的城市网格,每个格子有一个整数值:

  • 正数:居民区,数据需求为该值
  • 负数:可以建信号塔的位置,信号半径为
  • :空地

信号塔在 ,半径为 ,能覆盖 处的居民区,条件是欧几里得距离

服务价值规则:

  • 没被任何塔覆盖的居民区,价值为 0
  • 个塔同时覆盖的居民区,有效价值从 降为 (信号干扰)

目标:选择激活哪些塔,使总有效价值最大。在最大价值前提下,激活的塔数尽量少。

数据范围:,信号塔数量不超过 11。

思路

看到"信号塔数量不超过 11",这个限制非常关键。,直接枚举所有信号塔的激活方案完全可行。

先想清楚整个流程:

预处理覆盖关系

遍历网格,把所有塔和居民区分别收集出来。然后对于每个塔,算它能覆盖哪些居民区——判断欧几里得距离是否不超过半径即可。

这里有个小技巧:比较距离的时候不要开根号,直接比较距离的平方和半径的平方,既快又避免浮点误差。

枚举子集

用二进制掩码 从 0 到 枚举所有激活方案。对每个方案:

  1. 对每个居民区,数一下有多少个激活的塔覆盖了它(记为
  2. 如果 ,该居民区贡献
  3. 把所有居民区的贡献加起来就是总价值

维护全局最大价值和对应的最少塔数即可。

进一步优化

居民区数量可能很多(网格最大 ),但塔最多 11 个。对每个居民区预处理一个"哪些塔能覆盖它"的位掩码 ,这样在枚举子集时, 就能快速得到实际覆盖该居民区的塔集合,再用 popcount 数个数。

复杂度

  • 预处理覆盖关系:,其中
  • 枚举子集:,最坏

完全没问题。

代码

#include <bits/stdc++.h>
using namespace std;
int main(){
    int m, n;
    scanf("%d%d", &m, &n);
    vector<vector<int>> grid(m, vector<int>(n));
    vector<array<int,3>> towers, residents;
    for(int i = 0; i < m; i++)
        for(int j = 0; j < n; j++){
            scanf("%d", &grid[i][j]);
            if(grid[i][j] < 0) towers.push_back({i, j, -grid[i][j]});
            else if(grid[i][j] > 0) residents.push_back({i, j, grid[i][j]});
        }
    int T = towers.size();
    int R = residents.size();
    // cover[t][r] = 塔 t 是否覆盖居民区 r
    vector<vector<bool>> cover(T, vector<bool>(R, false));
    for(int t = 0; t < T; t++){
        int tx = towers[t][0], ty = towers[t][1];
        long long tr = towers[t][2];
        for(int r = 0; r < R; r++){
            int rx = residents[r][0], ry = residents[r][1];
            long long dx = tx - rx, dy = ty - ry;
            if(dx*dx + dy*dy <= tr*tr) cover[t][r] = true;
        }
    }
    long long bestVal = 0;
    int bestCnt = 0;
    for(int mask = 0; mask < (1 << T); mask++){
        int tc = __builtin_popcount(mask);
        long long val = 0;
        for(int r = 0; r < R; r++){
            int cnt = 0;
            for(int t = 0; t < T; t++){
                if((mask & (1 << t)) && cover[t][r]) cnt++;
            }
            if(cnt > 0) val += residents[r][2] / cnt;
        }
        if(val > bestVal || (val == bestVal && tc < bestCnt)){
            bestVal = val;
            bestCnt = tc;
        }
    }
    printf("%lld %d\n", bestVal, bestCnt);
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt(), n = sc.nextInt();
        List<int[]> towers = new ArrayList<>();
        List<int[]> residents = new ArrayList<>();
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++) {
                int v = sc.nextInt();
                if (v < 0) towers.add(new int[]{i, j, -v});
                else if (v > 0) residents.add(new int[]{i, j, v});
            }
        int T = towers.size(), R = residents.size();
        boolean[][] cover = new boolean[T][R];
        for (int t = 0; t < T; t++) {
            int tx = towers.get(t)[0], ty = towers.get(t)[1];
            long tr = towers.get(t)[2];
            for (int r = 0; r < R; r++) {
                int rx = residents.get(r)[0], ry = residents.get(r)[1];
                long dx = tx - rx, dy = ty - ry;
                if (dx * dx + dy * dy <= tr * tr) cover[t][r] = true;
            }
        }
        long bestVal = 0;
        int bestCnt = 0;
        for (int mask = 0; mask < (1 << T); mask++) {
            int tc = Integer.bitCount(mask);
            long val = 0;
            for (int r = 0; r < R; r++) {
                int cnt = 0;
                for (int t = 0; t < T; t++)
                    if ((mask & (1 << t)) != 0 && cover[t][r]) cnt++;
                if (cnt > 0) val += residents.get(r)[2] / cnt;
            }
            if (val > bestVal || (val == bestVal && tc < bestCnt)) {
                bestVal = val;
                bestCnt = tc;
            }
        }
        System.out.println(bestVal + " " + bestCnt);
    }
}
import sys

def main():
    data = sys.stdin.buffer.read().split()
    idx = 0
    m = int(data[idx]); idx += 1
    n = int(data[idx]); idx += 1
    towers = []
    residents = []
    for i in range(m):
        for j in range(n):
            v = int(data[idx]); idx += 1
            if v < 0:
                towers.append((i, j, -v))
            elif v > 0:
                residents.append((i, j, v))
    T = len(towers)
    R = len(residents)
    # 对每个居民区,预处理能覆盖它的塔的位掩码
    rcov = [0] * R
    for t in range(T):
        tx, ty, tr = towers[t]
        tr2 = tr * tr
        for r in range(R):
            rx, ry, rd = residents[r]
            dx, dy = tx - rx, ty - ry
            if dx*dx + dy*dy <= tr2:
                rcov[r] |= (1 << t)
    best_val = 0
    best_cnt = 0
    for mask in range(1 << T):
        tc = bin(mask).count('1')
        val = 0
        for r in range(R):
            active = rcov[r] & mask
            if active:
                cnt = bin(active).count('1')
                val += residents[r][2] // cnt
        if val > best_val or (val == best_val and tc < best_cnt):
            best_val = val
            best_cnt = tc
    print(best_val, best_cnt)

main()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', l => lines.push(l.trim()));
rl.on('close', () => {
    let idx = 0;
    const [m, n] = lines[idx++].split(' ').map(Number);
    const towers = [], residents = [];
    for (let i = 0; i < m; i++) {
        const row = lines[idx++].split(/\s+/).map(Number);
        for (let j = 0; j < n; j++) {
            if (row[j] < 0) towers.push([i, j, -row[j]]);
            else if (row[j] > 0) residents.push([i, j, row[j]]);
        }
    }
    const T = towers.length, R = residents.length;
    const cover = Array.from({length: T}, () => Array(R).fill(false));
    for (let t = 0; t < T; t++) {
        const [tx, ty, tr] = towers[t];
        for (let r = 0; r < R; r++) {
            const dx = tx - residents[r][0], dy = ty - residents[r][1];
            if (dx*dx + dy*dy <= tr*tr) cover[t][r] = true;
        }
    }
    let bestVal = 0, bestCnt = 0;
    for (let mask = 0; mask < (1 << T); mask++) {
        let tc = 0;
        for (let b = mask; b; b &= b-1) tc++;
        let val = 0;
        for (let r = 0; r < R; r++) {
            let cnt = 0;
            for (let t = 0; t < T; t++)
                if ((mask & (1 << t)) && cover[t][r]) cnt++;
            if (cnt > 0) val += Math.floor(residents[r][2] / cnt);
        }
        if (val > bestVal || (val === bestVal && tc < bestCnt)) {
            bestVal = val;
            bestCnt = tc;
        }
    }
    console.log(bestVal + ' ' + bestCnt);
});