2010-1141
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
'''题目大意'''[[BR]]
连通性dp经典模型稍微变形,把原来的正方形格子变成正六边形。
基本和HDU1693题目一样,每个可达格子需要且仅能用一个长度>=3的圈圈住。所有圈不能相交。
问不同的方案数。
'''解法'''[[BR]]
参考陈丹琦的国家队论文《基于连通性的状态压缩动态规划》和HDU1693的解题报告。
由于题目没有要求只能有一个圈,所以单位轮廓线的状态只需要0,1两种,表示该轮廓线是否被某个圈经过过。
因此,每个正六边形的六个边缘有且仅有两个状态为1。
与HDU1693不同的是,原来的是正方形的格子,现在是正六边形,所以轮廓线的长度与奇偶性相关,而且有点怪。
中间的状态转移,和对轮廓线的处理都需要人肉一下,具体参见程序和自己yy一下吧。。。
'''代码'''
{{{
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
vector<LL> V[2];
LL f[2][1 << 17];
int used[1 << 17];
int step, t1, t2;
bool map[9][8];
void print(int mask) {
for (int i = 0; i < 17; i++) {
printf("%d", mask & 1);
mask >>= 1;
}
}
inline void update(int now, int mask) {
if (used[now] == step) f[t2][now] += f[t1][mask];
else {
f[t2][now] = f[t1][mask];
used[now] = step;
V[t2].push_back(now);
}
}
int main() {
int n, m;
char st[10];
while (scanf("%d %d", &n, &m) != EOF) {
memset(map, true, sizeof(map));
for (int i = 0; i < m; i++) {
scanf("%s", st);
map[st[0] - 'A'][st[1] - 'A'] = false;
}
memset(used, 255, sizeof(used));
f[0][0] = 1;
V[0].clear(), V[0].push_back(0);
t1 = 0, step = -1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < 8; j++) {
step++;
t2 = 1 - t1;
V[t2].clear();
for (int k = 0; k < (int)V[t1].size(); k++) {
int mask = V[t1][k];
// 0 -> 0, 1, 2
if (j == 0) {
int now = ((mask >> 1) << 1) << 2; // (0)1...14 -> (000)3...16
if (!map[i][j]) {
if (mask & 1) continue;
update(mask << 2, mask);
}
else {
if (mask & 1) {
update(now | 1, mask);
update(now | 2, mask);
update(now | 4, mask);
}
else {
update(now | 3, mask);
update(now | 5, mask);
update(now | 6, mask);
}
}
continue;
}
// 14, 15, 16 -> 14
if (j == 7) {
int now = mask & ((1 << 14) - 1); // 0...16 -> 0...13
int left = mask >> 14;
if (!map[i][j]) {
if (left) continue;
update(now, mask);
}
else {
if (left == 3 || left == 5 || left == 6) {
update(now, mask);
} else if (left == 1 || left == 2 || left == 4) {
update(now | (1 << 14), mask);
}
}
continue;
}
if (j % 2 == 0) {
// 2 * j - 1, 2 * j -> 2 * j - 1, 2 * j, 2 * j + 1, 2 * j + 2
int now = mask & ((1 << (2 * j - 1)) - 1);
now |= (mask >> (2 * j + 1)) << (2 * j + 3);
int left = (mask >> (2 * j - 1)) & 3;
if (!map[i][j]) {
if (left) continue;
update(now, mask);
}
else {
if (left == 3) {
update(now, mask);
}
else if (left == 1 || left == 2) {
update(now | (1 << (2 * j - 1)), mask);
update(now | (2 << (2 * j - 1)), mask);
update(now | (4 << (2 * j - 1)), mask);
update(now | (8 << (2 * j - 1)), mask);
}
else if (left == 0){
update(now | (3 << (2 * j - 1)), mask);
update(now | (5 << (2 * j - 1)), mask);
update(now | (9 << (2 * j - 1)), mask);
update(now | (6 << (2 * j - 1)), mask);
update(now | (10 << (2 * j - 1)), mask);
update(now | (12 << (2 * j - 1)), mask);
}
}
}
else {
// 2 * j, 2 * j + 1, 2 * j + 2, 2 * j + 3 -> 2 * j, 2 * j + 1
int now = mask & ((1 << (2 * j)) - 1);
now |= (mask >> (2 * j + 4)) << (2 * j + 2);
int left = (mask >> (2 * j)) & ((1 << 4) - 1);
if (!map[i][j]) {
if (left) continue;
update(now, mask);
}
else {
if (left == 0) {
update(now | (3 << (2 * j)), mask);
} else if (left == 1 || left == 2 || left == 4 || left == 8) {
update(now | (1 << (2 * j)), mask);
update(now | (2 << (2 * j)), mask);
} else if (left == 3 || left == 5 || left == 9 || left == 6 || left == 10 || left == 12) {
update(now, mask);
}
}
}
}
t1 = t2;
}
}
if (used[0] == step) {
printf("%lld\n",f[t1][0]);
}
else {
puts("0");
}
}
return 0;
}
}}}
题目大意
连通性dp经典模型稍微变形,把原来的正方形格子变成正六边形。
基本和HDU1693题目一样,每个可达格子需要且仅能用一个长度>=3的圈圈住。所有圈不能相交。
问不同的方案数。
解法
参考陈丹琦的国家队论文《基于连通性的状态压缩动态规划》和HDU1693的解题报告。
由于题目没有要求只能有一个圈,所以单位轮廓线的状态只需要0,1两种,表示该轮廓线是否被某个圈经过过。
因此,每个正六边形的六个边缘有且仅有两个状态为1。
与HDU1693不同的是,原来的是正方形的格子,现在是正六边形,所以轮廓线的长度与奇偶性相关,而且有点怪。
中间的状态转移,和对轮廓线的处理都需要人肉一下,具体参见程序和自己yy一下吧。。。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
vector<LL> V[2];
LL f[2][1 << 17];
int used[1 << 17];
int step, t1, t2;
bool map[9][8];
void print(int mask) {
for (int i = 0; i < 17; i++) {
printf("%d", mask & 1);
mask >>= 1;
}
}
inline void update(int now, int mask) {
if (used[now] == step) f[t2][now] += f[t1][mask];
else {
f[t2][now] = f[t1][mask];
used[now] = step;
V[t2].push_back(now);
}
}
int main() {
int n, m;
char st[10];
while (scanf("%d %d", &n, &m) != EOF) {
memset(map, true, sizeof(map));
for (int i = 0; i < m; i++) {
scanf("%s", st);
map[st[0] - 'A'][st[1] - 'A'] = false;
}
memset(used, 255, sizeof(used));
f[0][0] = 1;
V[0].clear(), V[0].push_back(0);
t1 = 0, step = -1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < 8; j++) {
step++;
t2 = 1 - t1;
V[t2].clear();
for (int k = 0; k < (int)V[t1].size(); k++) {
int mask = V[t1][k];
// 0 -> 0, 1, 2
if (j == 0) {
int now = ((mask >> 1) << 1) << 2; // (0)1...14 -> (000)3...16
if (!map[i][j]) {
if (mask & 1) continue;
update(mask << 2, mask);
}
else {
if (mask & 1) {
update(now | 1, mask);
update(now | 2, mask);
update(now | 4, mask);
}
else {
update(now | 3, mask);
update(now | 5, mask);
update(now | 6, mask);
}
}
continue;
}
// 14, 15, 16 -> 14
if (j == 7) {
int now = mask & ((1 << 14) - 1); // 0...16 -> 0...13
int left = mask >> 14;
if (!map[i][j]) {
if (left) continue;
update(now, mask);
}
else {
if (left == 3 || left == 5 || left == 6) {
update(now, mask);
} else if (left == 1 || left == 2 || left == 4) {
update(now | (1 << 14), mask);
}
}
continue;
}
if (j % 2 == 0) {
// 2 * j - 1, 2 * j -> 2 * j - 1, 2 * j, 2 * j + 1, 2 * j + 2
int now = mask & ((1 << (2 * j - 1)) - 1);
now |= (mask >> (2 * j + 1)) << (2 * j + 3);
int left = (mask >> (2 * j - 1)) & 3;
if (!map[i][j]) {
if (left) continue;
update(now, mask);
}
else {
if (left == 3) {
update(now, mask);
}
else if (left == 1 || left == 2) {
update(now | (1 << (2 * j - 1)), mask);
update(now | (2 << (2 * j - 1)), mask);
update(now | (4 << (2 * j - 1)), mask);
update(now | (8 << (2 * j - 1)), mask);
}
else if (left == 0){
update(now | (3 << (2 * j - 1)), mask);
update(now | (5 << (2 * j - 1)), mask);
update(now | (9 << (2 * j - 1)), mask);
update(now | (6 << (2 * j - 1)), mask);
update(now | (10 << (2 * j - 1)), mask);
update(now | (12 << (2 * j - 1)), mask);
}
}
}
else {
// 2 * j, 2 * j + 1, 2 * j + 2, 2 * j + 3 -> 2 * j, 2 * j + 1
int now = mask & ((1 << (2 * j)) - 1);
now |= (mask >> (2 * j + 4)) << (2 * j + 2);
int left = (mask >> (2 * j)) & ((1 << 4) - 1);
if (!map[i][j]) {
if (left) continue;
update(now, mask);
}
else {
if (left == 0) {
update(now | (3 << (2 * j)), mask);
} else if (left == 1 || left == 2 || left == 4 || left == 8) {
update(now | (1 << (2 * j)), mask);
update(now | (2 << (2 * j)), mask);
} else if (left == 3 || left == 5 || left == 9 || left == 6 || left == 10 || left == 12) {
update(now, mask);
}
}
}
}
t1 = t2;
}
}
if (used[0] == step) {
printf("%lld\n",f[t1][0]);
}
else {
puts("0");
}
}
return 0;
}