状态压缩DP->炮兵阵地(Acwing)

https://www.acwing.com/problem/content/description/294/

题意:n,m平面内,有些格子可以放士兵,士兵的攻击范围是一个十字形,在士兵不攻击其他士兵的前提下,求方格内最多放置的士兵数。 n <= 100, m <= 10

分析:状压dp,状态是三维,dp[row][i][j]表示第row行以i排列放置,row - 1行以j排列放置的最多士兵数。 容易想到转移dp[row][i][j] <- dp[row - 1][j][k] 满足(i & j) == 0, (i & k) == 0, (k & j) == 0。在这种前提条件下,还要满足行((i <<(>>) 1(2)) & i) == 0。很容易列出枚举所有状态进行递推的代码:

void solve(){
	int n, m;
	cin >> n >> m;

	vector<vector<char>> grid(n + 1, vector<char>(m + 1));
	for (int i = 1; i <= n; ++i){
		for (int j = 1; j <= m; ++j){
			cin >> grid[i][j];
		}
	}

	auto valid = [&](int x){
	   for (int j = 1; j <= 2; ++j){
			if (((x >> j) & x) || ((x << j) & x)) {
				return false;
			}
	   }
	   return true;
	};

	auto cnt = [&](int row, int x)->int{
		int res = 0;
		for (int i = 0; i < m; ++i){
			res += (((x >> i) & 1) && grid[row][i + 1] == 'P');
		}
		return res;
	};

	vector<vector<vector<int>>> dp(2, vector<vector<int>> (1 << m, vector<int>(1 << m)));
	int cur = 1;
	int ans = 0;
	for (int row = 1; row <= n; ++row){
		for (int i = 0; i < (1 << m); ++i){
			if (valid(i)){
				for (int j = 0; j < (1 << m); ++j){
					if (valid(j) && ((i & j) == 0)){
						if (row == 1){
							dp[cur][i][j] = cnt(row, i);
							ans = max(ans, dp[cur][i][j]);
							continue;
						}
						int tmp = 0;
						for (int k = 0; k < (1 << m); ++k){
							if (valid(k) && (((i & k) == 0) && ((j & k) == 0))){
								tmp = max(tmp, dp[cur ^ 1][j][k]);
							}
						}
						dp[cur][i][j] = tmp + cnt(row, i);
						ans = max(dp[cur][i][j], ans);
					}
				}
			}
		}
		cur ^= 1;
	}

	cout << ans << '\n';
}

然而枚举所有状态的时间复杂度是pow(1 << 10, 3) ≈ 1e9
所以有一个优化的点就是直接剔除掉不合法的行状态,对于输入m,先枚举出所有合法的行状态,然后在合法的行状态中进行转移:

void solve(){
    int n, m;
    cin >> n >> m;

    vector<vector<char>> grid(n + 1, vector<char>(m + 1));
    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= m; ++j){
            cin >> grid[i][j];
        }
    }

    auto valid = [&](int x){
       for (int j = 1; j <= 2; ++j){
            if (((x >> j) & x) || ((x << j) & x)) {
                return false;
            }
       }
       return true;
    };

    vector<int> valid_state;
    for(int i = 0; i < (1 << m); ++i){
        if (valid(i)){
            valid_state.emplace_back(i);
        }
    }


    auto cnt = [&](int row, int x)->int{
        int res = 0;
        for (int i = 0; i < m; ++i){
            res += (((x >> i) & 1) && grid[row][i + 1] == 'P');
        }
        return res;
    };

    vector<vector<vector<int>>> dp(2, vector<vector<int>> (1 << m, vector<int>(1 << m)));
    int cur = 1;
    int ans = 0;
    for (int row = 1; row <= n; ++row){
        for (auto& i : valid_state){
            for (auto& j : valid_state){
                if ((i & j) == 0){
                    if (row == 1){
                        dp[cur][i][j] = cnt(row, i);
                        ans = max(ans, dp[cur][i][j]);
                    }
                    else{
                        int temp = 0;
                        for (auto& k : valid_state){
                            if (((i & k) == 0) && ((j & k) == 0)){
                                temp = max(temp, dp[cur ^ 1][j][k]);
                            }
                        }
                        dp[cur][i][j] = temp + cnt(row, i);
                        ans = max(ans, dp[cur][i][j]);
                    }
                }

            }
        }
        cur ^= 1;
    }

    cout << ans << '\n';
}

AC

posted @ 2024-01-16 11:13  _Yxc  阅读(17)  评论(0)    收藏  举报