题解 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\n", popcount(way[i]));
            for (int j = 0; j < 16; j++) 
                if (way[i] & (1 << j))
                    printf("%d %d\n", (j >> 2) + 1, (j & 3) + 1);
            break;
        }
    return 0;
}

// Create File Date : 2020-06-02
posted @ 2020-06-03 08:21  航空信奥  阅读(190)  评论(0编辑  收藏  举报