Maximum Non-Attacking Artillery Units on Terrain Map

Problem Description

Commanding officers need to deploy artillery units on an N×M grid map. The map consists of N rows and M columns, with each cell being either mountainous terrain (denoted by 'H') or plain terrain (denoted by 'P'). Artillery units can only be placed on plain terrain, with a maximum of one unit per plain cell.

The attack range of an artillery unit extends two cells horizontally (left and right) and two cells vertically (up and down) from its position. Importantly, the attack range is not affected by terrain type. The challenge is to deploy the maximum number of artillery units such that no unit is within the attack range of another unit.

Input

The first line contains two positive integers N and M, separated by a space. The following N lines each contain M consecutive characters representing the map data, where each character is either 'P' (plain) or 'H' (mountain).

Constraints: N ≤ 100, M ≤ 10

Output

A single integer representing the maximum number of artillery units that can be deployed without violating the non-attack constraint.

Sample Input

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

Sample Output

6

Solution Approach

This problem can be solved using dynamic programming with bitmasking. The key insight is that since M is small (≤ 10), we can represent each row's artillery configuration as a bitmask where each bit indicates whether an artillery unit is placed in that column.

First, we preprocess all possible valid configurations for a single row (configurations where no two artillery units attack each other). Then, we use dynamic programming to consider valid configurations across three consecutive rows to ensure no attacks between them.

Implementation

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

const int MAX_ROWS = 110;
const int MAX_CONFIGS = 1000;

bool hasConflict(int config) {
    // Check if any artillery units in this configuration would attack each other
    if ((config & (config << 1)) || (config & (config << 2))) {
        return true;
    }
    return false;
}

int countUnits(int config) {
    // Count the number of artillery units in the configuration
    int count = 0;
    while (config) {
        count++;
        config = config & (config - 1);
    }
    return count;
}

int n, m;
vector<int> validConfigs;
int unitCounts[MAX_CONFIGS];
int terrain[MAX_ROWS];
int dp[MAX_ROWS][MAX_CONFIGS][MAX_CONFIGS];

bool isOnMountain(int row, int config) {
    // Check if any artillery unit is placed on a mountain
    return (terrain[row] & config) != 0;
}

bool areCompatible(int config1, int config2, int config3) {
    // Check if three configurations can coexist without attacks
    if ((config1 & config2) || (config1 & config3) || (config2 & config3)) {
        return false;
    }
    return true;
}

void initializeValidConfigs() {
    // Generate all valid configurations for a single row
    for (int i = 0; i < (1 << m); i++) {
        if (!hasConflict(i)) {
            validConfigs.push_back(i);
            unitCounts[validConfigs.size() - 1] = countUnits(i);
        }
    }
}

int main() {
    scanf("%d %d", &n, &m);
    
    // Read terrain data
    for (int i = 0; i < n; i++) {
        string row;
        cin >> row;
        for (int j = 0; j < m; j++) {
            if (row[j] == 'H') {
                terrain[i] |= (1 << (m - 1 - j));
            }
        }
    }
    
    initializeValidConfigs();
    int numConfigs = validConfigs.size();
    
    // Initialize DP table for the first row
    for (int i = 0; i < numConfigs; i++) {
        int currentConfig = validConfigs[i];
        if (!isOnMountain(0, currentConfig)) {
            dp[0][i][0] = unitCounts[i];
        }
    }
    
    // Fill DP table for subsequent rows
    for (int row = 1; row < n; row++) {
        for (int i = 0; i < numConfigs; i++) { // Previous row config
            int prevConfig1 = validConfigs[i];
            if (isOnMountain(row - 1, prevConfig1)) continue;
            
            for (int j = 0; j < numConfigs; j++) { // Current row config
                int currentConfig = validConfigs[j];
                if (isOnMountain(row, currentConfig)) continue;
                
                // Check compatibility with two rows above
                if (row >= 2) {
                    for (int k = 0; k < numConfigs; k++) {
                        int prevConfig2 = validConfigs[k];
                        if (isOnMountain(row - 2, prevConfig2)) continue;
                        
                        if (areCompatible(prevConfig2, prevConfig1, currentConfig)) {
                            dp[row][j][i] = max(dp[row][j][i], dp[row-1][i][k] + unitCounts[j]);
                        }
                    }
                } else {
                    // For the second row, only check compatibility with the row above
                    if ((prevConfig1 & currentConfig) == 0) {
                        dp[row][j][i] = max(dp[row][j][i], unitCounts[j]);
                    }
                }
            }
        }
    }
    
    // Find the maximum number of artillery units
    int maxUnits = 0;
    for (int i = 0; i < numConfigs; i++) {
        for (int j = 0; j < numConfigs; j++) {
            maxUnits = max(maxUnits, dp[n-1][i][j]);
        }
    }
    
    printf("%d\n", maxUnits);
    return 0;
}

Tags: Dynamic Programming bitmask grid optimization

Posted on Wed, 02 Sep 2026 16:28:43 +0000 by usamaalam