[CODEVS 1050] 棋盘染色 2
描述
有一个5*N的棋盘,棋盘中的一些格子已经被染成了黑色,你的任务是对最少的格子染色,使得所有的黑色能连成一块。
分析
CODEVS 题解里有个很良心的人, 我是看了他的才写的.
http://codevs.cn/wiki/solution/?problem_id=1050
Solution_ID:5329轮廓线DP.
我开始错在了把 normalize(b) 放在了if前面, 导致if判断打了酱油…采用四进制 (两个二进制位) 表示轮廓线的状态, 0为白色格子, 1、2、3为不同的黑格子.
main() :
if (x, y) 是黑点
if 当前状态 (x, y), 上方的点为黑点:
获取其强联通分量的编号, 将当前的格子设为与其相同的强联通分量编号.
else if 当前状态 (x, y) 中 y > 0 && 左边的点为黑点:
获取其强联通分量的编号, 将当前的格子设为与其相同的强联通分量编号.
else: 自己作为一个单独的强联通分量(编号为3).else :
1. 不更改, 直接加入
2. 改成1 =>
if 当前状态 (x, y), 上方的点为黑点:
获取其强联通分量的编号, 将当前的格子设为与其相同的强联通分量编号.
else if 当前状态 (x, y), y > 0 && 左边的点为黑点:
获取其强联通分量的编号, 将当前的格子设为与其相同的强联通分量编号.
else: 自己作为一个单独的强联通分量a 和 b 的 &3 后的结果相差的是什么? => 两个相邻的格子吧 => 既然相邻了又都是黑色的, 那么应属于一个强联通分量 => 合并两个强联通分量.
这里有点乱 = =
当前格子左上方的格子将要退出轮廓线, 如果它消失, 它所在的强联通分量可能就会消失, 导致后面出现新的多余的强联通分量.
=>
if 左上方的格子不是空的, 并且 b 中没有与它相同强联通分量编号的格子了. 说明 b 状态不合法 不能更新答案.而如果轮廓线上没有最初编号为1的格子, 后面的格子一定不能与前面的格子相连通了, 也不是合法的状态.
满足几个要求就可以更新答案了.
用到的位运算
- << 2 // 转移到下个状态.
- >> 8 // 取出当前格子上面格子的强联通分量编号.
- &3 // 取出当前格子左面格子的强联通分量编号.
- x^(a << i)^(b << i) // 把x从第i位开始的两位由a变为b (a, b都是两个二进制位).
代码
127ms 492kB
简化了一下代码.
看了看代码高亮发现 state 和 read 好像都是保留字啊, 以后不用了…
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100 + 10;
int cur, board[maxn][5], f[2][1<<15];
bool appear;
void merge(int& state, int a, int b) {
for(int i = 0; i < 12; i+=2)
if(((state>>i) & 3) == a) state = state ^ (a<<i) ^ (b<<i);
}
bool exist(int state, int color) {
for(int i = 0; i < 10; i+=2)
if(((state>>i) & 3) == color) return 1;
return 0;
}
void normalize(int& state) {
if(!exist(state, 2) && exist(state, 3)) {
if(appear) merge(state, 3, 2);
else { merge(state, 3, 1); appear = 1; }
}
}
void update(int a, int b, int row, int col) {
if((a&3) > 0 && (b&3) > 0 && col > 0 && (a&3) != (b&3))
merge(b, max(a&3, b&3), min(a&3, b&3));
if(a == 0 || (((b>>10) <= 0 || exist(b, b>>10)) && (((b>>10) == 1) || exist(b, 1)))) {
normalize(b);
int t = (board[row][col] == 1 || (b&3) == 0) ? 0 : 1;
b ^= (b>>10) << 10;
f[cur][b] = min(f[cur][b], f[1-cur][a] + t);
}
}
int main() {
int n, lasti = 0, lastj = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
char read[5];
scanf("%s", read);
for(int j = 0; j < 5; j++) {
board[i][j] = read[j] - '0';
if(board[i][j]) { lasti = i; lastj = j; }
}
}
memset(f, 0x7f, sizeof(f));
f[cur][0] = 0;
for(int i = 0; i < n; i++) if(i <= lasti)
for(int j = 0; j < 5; j++) {
if(!board[i][j] && !appear) continue;
if(i == lasti && j > lastj) break;
cur ^= 1;
memset(f[cur], 0x7f, sizeof(f[cur]));
for(int k = 0; k < (1<<10); k++) {
if((k>>8) > 0) update(k, (k<<2)^(k>>8), i, j);
else if((k&3) > 0 && j) update(k, (k<<2)^(k&3), i, j);
else update(k, (k<<2)^3, i, j); // new color
if(!board[i][j]) update(k, k<<2, i, j);
}
}
int ans = 0x7fffffff;
for(int state = 0; state < (1<<10)-1; state++)
if(!exist(state, 2) && !exist(state, 3))
ans = min(ans, f[cur][state]);
printf("%d\n", ans);
return 0;
}