[CODEVS 1050] 棋盘染色 2

来源:互联网 发布:淘宝延长收货三天 编辑:程序博客网 时间:2024/06/09 23:46

描述

有一个5*N的棋盘,棋盘中的一些格子已经被染成了黑色,你的任务是对最少的格子染色,使得所有的黑色能连成一块。

http://codevs.cn/problem/1050/


分析

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的格子, 后面的格子一定不能与前面的格子相连通了, 也不是合法的状态.

满足几个要求就可以更新答案了.

用到的位运算

  1. << 2 // 转移到下个状态.
  2. >> 8 // 取出当前格子上面格子的强联通分量编号.
  3. &3 // 取出当前格子左面格子的强联通分量编号.
  4. 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;}
1 0
原创粉丝点击