https://niumacode.com/training/144/problem/P1722
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
int n, m;
vector<pair<int, int>> stars; // 存储所有标记点坐标
unordered_set<unsigned long long> final_states; // 存储最终状态
// 检查坐标是否在网格内
bool is_valid(int x, int y) {
return x >= 0 && x < n && y >= 0 && y < m;
}
// 深度优先搜索:index为当前处理的标记点索引,mask为当前覆盖状态
void dfs(int index, unsigned long long mask) {
// 处理完所有标记点,检查是否为最终状态
if (index == stars.size()) {
final_states.insert(mask);
return;
}
int x = stars[index].first;
int y = stars[index].second;
int pos = x * m + y; // 当前标记点在mask中的位索引
// 情况1:不放置以当前标记点为端点的石柱,直接处理下一个标记点
dfs(index + 1, mask);
// 情况2:尝试放置1x3或3x1石柱,以当前标记点为一端
// 方向1:向右放置1x3石柱(覆盖(x,y), (x,y+1), (x,y+2))
if (is_valid(x, y + 1) && is_valid(x, y + 2)) {
int p1 = x * m + (y + 1);
int p2 = x * m + (y + 2);
if (!(mask & (1ULL << pos)) && !(mask & (1ULL << p1)) && !(mask & (1ULL << p2))) {
unsigned long long new_mask = mask | (1ULL << pos) | (1ULL << p1) | (1ULL << p2);
dfs(index + 1, new_mask);
}
}
// 方向2:向左放置1x3石柱(覆盖(x,y-2), (x,y-1), (x,y))
if (is_valid(x, y - 1) && is_valid(x, y - 2)) {
int p1 = x * m + (y - 1);
int p2 = x * m + (y - 2);
if (!(mask & (1ULL << pos)) && !(mask & (1ULL << p1)) && !(mask & (1ULL << p2))) {
unsigned long long new_mask = mask | (1ULL << pos) | (1ULL << p1) | (1ULL << p2);
dfs(index + 1, new_mask);
}
}
// 方向3:向下放置3x1石柱(覆盖(x,y), (x+1,y), (x+2,y))
if (is_valid(x + 1, y) && is_valid(x + 2, y)) {
int p1 = (x + 1) * m + y;
int p2 = (x + 2) * m + y;
if (!(mask & (1ULL << pos)) && !(mask & (1ULL << p1)) && !(mask & (1ULL << p2))) {
unsigned long long new_mask = mask | (1ULL << pos) | (1ULL << p1) | (1ULL << p2);
dfs(index + 1, new_mask);
}
}
// 方向4:向上放置3x1石柱(覆盖(x-2,y), (x-1,y), (x,y))
if (is_valid(x - 1, y) && is_valid(x - 2, y)) {
int p1 = (x - 1) * m + y;
int p2 = (x - 2) * m + y;
if (!(mask & (1ULL << pos)) && !(mask & (1ULL << p1)) && !(mask & (1ULL << p2))) {
unsigned long long new_mask = mask | (1ULL << pos) | (1ULL << p1) | (1ULL << p2);
dfs(index + 1, new_mask);
}
}
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; ++i) {
string s;
cin >> s;
for (int j = 0; j < m; ++j) {
if (s[j] == '*') {
stars.emplace_back(i, j);
}
}
}
dfs(0, 0); // 初始状态:无任何覆盖,从第0个标记点开始处理
cout << final_states.size() << endl;
return 0;
}
与、或、异或运算
这三种运算都是两数间运算,将两个整数作为二进制数,对每一位逐一运算。
与运算(&):对应位都为 1 时结果为 1,如3 & 5 = 1(011 & 101 = 001)。
或运算(|):对应位有一个为 1 时结果为 1,如3 | 5 = 7(011 | 101 = 111)。
异或运算(^):对应位不同时结果为 1,如3 ^ 5 = 6(011 ^ 101 = 110),异或运算的逆运算是它本身,即a ^ b ^ b = a。
左移和右移运算
num << i表示将num的二进制表示向左移动i位,右侧补 0。如1 << 2 = 4(0001左移 2 位变为0100)。
num >> i表示将num的二进制表示向右移动i位,正数左侧补 0,负数左侧补 1(符号位不变)。如4 >> 1 = 2(0100右移 1 位变为0010)。
移位运算中,右操作数为负值或大于等于左操作数的位数时,行为未定义,如int类型变量a,a << -1和a << 32都是未定义的。
常考结论与模板
按位与,越与越小:对于子数组l~r的元素按位与(记作&[l,r]),则一定有&[l,r+1]<=&[l,r]并且&[l-1,r]<=&[l,r]
按位或,越或越大:对于子数组l~r的元素按位或(记作|[l,r]),则一定有|[l,r+1]>=|[l,r]并且|[l-1,r]<|[l,r]
获取x对应的二进制从低到高(从右向左)的第i位
int getBit(int x, int i) { return (x >> i) & 1; }
常用于判断整数x的每一位是否是1(记得加括号!!)
for (int i = 0; i < 32; i++) {// i代表第几个bit位,一般对于1e9的数据范围,直接枚举到32即可
if ((x >> i) & 1) {
//说明x从低到高第i位位1,可以做对应处理
}
}
对于n个物品,枚举其从全不选到全选的2^n种状态
for (int state = 0; state < (1 << n); state++) {
// 处理 state
}
枚举一个数x的所有非空子集
for (int state = x; state; state = (state - 1) & x) {
// 处理 state 的逻辑
}
枚举一个数x的所有超集
for (int state = x; state < (1 << n); state = (state + 1) | x) {
// 处理 state 的逻辑
}
返回x对应的二进制中1的个数(该方法的时间复杂度可以认为近似O(1),会带个小常数)
int main() {
int x = 15;
int count = __builtin_popcount(x);
printf("The number of 1 bits in %u is %d\n", x, count);// 输出4
return 0;
}
lowbit操作:返回x对应的二进制最低(最右)1及其后面的 0 所构成的数字(常用于树状数组中)
int lowbit(int x) {
return x & -x;
}
浙公网安备 33010602011771号