[pieces] 画家问题 平方指数到线性指数枚举 两种解法

问题描述

有一个正方形的墙,由N*N个正方形的砖组成,其中一些砖是白色的,另外一些砖是黄色的。Bob是个画家,想把全部的砖都涂成黄色。但他的画笔不好使。当他用画笔涂画第(i, j)个位置的砖时, 位置(i-1, j)、 (i+1, j)、 (i, j-1)、 (i, j+1)上的砖都会改变颜色。请你帮助Bob计算出最少需要涂画多少块砖,才能使所有砖的颜色都变成黄色。
题图
这个题本来如果枚举整个棋盘的话,为 O ( 2 n 2 ) O(2^{n^2})
但我们可以发现一个关系,在前一行没有涂黄的格子对应下一行,如果可以涂色的话,那么上一行就圆满了!!
这就和我们的递推想法达成了一致。递推就是用先前确定的值来表示新的case,在这里也就是说,通过这样的规则,我们只需要枚举第一行,整个画板达成全黄的所有方案就已经被枚举过了。
如果枚举第一行的 2 n 2^{n} 种方案的某种方案不能使得最后一行全黄,说明这种方法行不通。
于是有如下:

示例代码

示例代码表现

#include <iostream>
#include <cstring>
using namespace std;

bool orig[17][17], map[17][17], res[17][17];
int n;

void init()
{
    cin >> n;
    char tmp;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
        {
            cin >> tmp;
            if (tmp == 'w')
                orig[i][j] = 1;
        }
}

int ptoi(bool *pressed)
{
    int tmp = 0;
    for (int i = 0; i < n; i++)
        if (pressed[i])
            tmp |= (1<<i);
    return tmp;
}

int itop(int val, bool *isPressed)
{
    for (int i = 0; i < n; i++)
        if (val & (1 << i))
            isPressed[i] = true;
        else isPressed[i] = false;
}

void turn(int i, int j)//对第j行第k列的格子进行涂色操作
{
    if (j > 0)//从第二行开始。i已经保证大于零了,所以不用判断
        map[i][j-1] ^= true;//异或运算使对应格子颜色反转
    map[i][j] ^= true;
    if (j < n-1)
        map[i][j+1] ^= true;
    if (i < n-1)
        map[i+1][j] ^= true;
}

void solve()
{
    int ans = 15 * 15 + 10;
    for (int i = 0; i < (1<<n); i++)//枚举第一行的所有可能方案
    {
        memcpy(map, orig, sizeof orig);
        int p = i;
        for (int j = 0; j < n; j++)//逐行完善枚举结果
        {
            itop(p, res[j]);
            for (int k = 0; k < n; k++)//实施枚举结果,并传递信息给下一次
                if (res[j][k])
                    turn(j, k);
            p = ptoi(map[j]);
        }
        if (p == 0)//退出时,p代表最后一行的信息,尝试更新结果
        {
            int cnt = 0;
            for (int j = 0; j < n; j++)
                for (int k = 0; k < n; k++)
                    if (res[j][k])  cnt++;
            if (cnt < ans)
                ans = cnt;
        }
    }
    if (ans < 235)
        cout << ans;
    else cout << "inf";
}

int main()
{
    init();
    solve();
}

优化代码:

  • 减少了前后传递信息的过程中中利用数组做中介的愚蠢行为
  • 同时使用inline函数(虽然函数比较长……且算空间换时间)加inline的优化效果并不明显(仅个位数ms)。

优化结果

#include <iostream>
#include <cstring>
using namespace std;

bool orig[17][17], map[17][17], res[17][17];
int n;

void init()
{
    cin >> n;
    char tmp;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
        {
            cin >> tmp;
            if (tmp == 'w')
                orig[i][j] = 1;
        }
}

void itop(int val, bool *isPressed)//将一个数字表示为二进制数填入数组中
{
    for (int i = 0; i < n; i++)
        if (val & (1 << i))
            isPressed[i] = true;
        else isPressed[i] = false;
}

inline void turn(int i, int j)//对第j行第k列的格子进行涂色操作
{
    if (j > 0)//从第二行开始。i已经保证大于零了,所以不用判断
        map[i][j-1] ^= true;//异或运算使对应格子颜色反转
    map[i][j] ^= true;
    if (j < n-1)
        map[i][j+1] ^= true;
    if (i < n-1)
        map[i+1][j] ^= true;
}

void solve()
{
    int ans = 15 * 15 + 50;
    for (int i = 0; i < (1<<n); i++)//枚举第一行的所有可能方案
    {
        memcpy(map, orig, sizeof orig);
        itop(i, res[0]); //将第一行转化成对应操作记录
        for (int j = 0; j < n; j++) 
            if (res[0][j]) turn(0, j); //对图纸进行更新
        for (int j = 1; j < n; j++)//逐行完善枚举结果
            for (int k = 0; k < n; k++)//实施枚举结果,并传递信息给下一次
                if (map[j-1][k])
                    res[j][k] = 1, turn(j, k);
                else res[j][k] = 0; //对于多case的枚举,最好使用防御式编程,尽可能不要简省else
        int flag = 1;
        for (int j = 0; j < n; j++)
            if (map[n-1][j])
                flag = 0;//检查最后一行是否已经全部涂黄
        if (flag) //如果有一个没有涂好,替代法一中的ptoi
        {
            int cnt = 0;
            for (int j = 0; j < n; j++)
                for (int k = 0; k < n; k++)
                    if (res[j][k])  cnt++;
            if (cnt < ans)
                ans = cnt;
        }
    }
    if (ans < 275)
        cout << ans;
    else cout << "inf";
}

int main()
{
    init();
    solve();
}

猜你喜欢

转载自blog.csdn.net/weixin_45502929/article/details/106672392