码迷,mamicode.com
首页 > 其他好文 > 详细

[CODEVS 1050] 棋盘染色 2

时间:2015-02-20 19:45:28      阅读:408      评论:0      收藏:0      [点我收藏+]

标签:轮廓线动态规划

描述

有一个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;
}

[CODEVS 1050] 棋盘染色 2

标签:轮廓线动态规划

原文地址:http://blog.csdn.net/qq_21110267/article/details/43889525

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!