题解 POJ 2985 The Pilots Brothers' refrigerator

暴力出奇迹

考虑将每个位置拨 (/) 不拨表示为 (1) (/) (0),那么 (n^2) 个位置的情况就可以用一个 (n^2=16) 位无符号二进制数表示。

考虑对于一种拨开关的方案,可以用 (Theta(n^3)(n=4)) 的时间检验是否可行((Theta(n^2)) 枚举每一个位置, (Theta(n)) 进行状态翻转操作)

考虑枚举所有拨的方案,并对于每一种方案进行检查是否可行,总时间复杂度 (Theta(2^{n^2}n^3)),由于 (n) 很小,可以通过本题。

考虑题目要求的是最优化方案,所以还不能简单地把拨的方案从 (0)(2^{16}-1) 枚举。

考虑先检验的方案,其二进制下的 (operatorname{popcount}) 值应当尽量的小((operatorname{popcount}) 表示一个数二进制下各位上 (1) 的个数)

考虑对于一个表示检验顺序的数组 way(0..65535),我们在将其初始化 for (int i = 0; i < 65536; i++) way[i] = i; 后应当对其按照 (operatorname{popcount}) 值排序。

考虑编写自定义 (operatorname{sort}) 比较函数 PopcountComp 为:

1 bool PopcountComp(int a, int b) {
2    return popcount(a) < popcount(b); 
3 }

如果使用 (STL) 中的 __builtin_popcount 函数,真实比赛会 (CE),因此考虑手打 popcount

1 int Stable[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
2 int popcountS(int a) { return Stable[a >> 4] + Stable[a & 15]; }
3 int popcount(int a) { return popcountS(a >> 8) + popcountS(a & 255); }

此程序第一行为一个手打的 0~15 的 popcount 表,popcountS 通过使用 Stable 中的数据处理 0~255 的数的 popcount 值,popcount 则通过调用 popcountS 处理 0~65535 的数的 popcount 值。
值得一提的是,(STL) 中的 popcount 是类似的写法,不过用于查询的表的大小是 (256),从而使时间减少了一倍。

总时间复杂度为 (Theta(2^{n^2}n^3 + 2^{n^2}log_2{(2^{n^2})}) = Theta(2^{n^2}n^3 + 2^{n^2}n^2) = Theta(2^{n^2}n^3))

代码

已略去头文件 & I/O优化

//     自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只
// 要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也
// 能够保持自己的本色走下去。                               ——陈立杰
/*************************************
 * @problem:      2965: The Pilots Brothers' refrigerator.
 * @user_name:    HKXA0933.
 * @time:         2020-06-02.
 * @language:     C++.
 * @upload_place: POJ.
*************************************/ 

char t[5];
bool status[4][4], tmp[4][4];
int way[65536];

int Stable[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
int popcountS(int a) { return Stable[a >> 4] + Stable[a & 15]; }
int popcount(int a) { return popcountS(a >> 8) + popcountS(a & 255); }
bool PopcountComp(int a, int b) { return popcount(a) < popcount(b); }

bool check(int way) {
    memcpy(tmp, status, sizeof(status));
    for (int i = 0; i < 16; i++) 
        if (way & (1 << i)) {
            int x = i >> 2, y = i & 3;
            tmp[x][0] ^= 1; tmp[x][1] ^= 1; tmp[x][2] ^= 1; tmp[x][3] ^= 1;
            tmp[0][y] ^= 1; tmp[1][y] ^= 1; tmp[2][y] ^= 1; tmp[3][y] ^= 1;
            tmp[x][y] ^= 1;
        }
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            if (!tmp[i][j]) return false;
    return true;
}

signed main() {
    for (int i = 0; i < 4; i++) {
        scanf("%s", t);
        for (int j = 0; j < 4; j++)
            status[i][j] = t[j] == '-';
    }
    for (int i = 0; i < 65536; i++) way[i] = i;
    sort(way, way + 65536, PopcountComp);
    for (int i = 0; i < 65536; i++)
        if (check(way[i])) {
            printf("%d
", popcount(way[i]));
            for (int j = 0; j < 16; j++) 
                if (way[i] & (1 << j))
                    printf("%d %d
", (j >> 2) + 1, (j & 3) + 1);
            break;
        }
    return 0;
}

// Create File Date : 2020-06-02