拨钟问题 OpenJ_Bailian - 2814 (枚举)

https://vjudge.net/problem/OpenJ_Bailian-2814

这是一道和画家问题,熄灯问题极为相似的题目。

共同特性:操作对环境的改变是无序的,每个操作都会影响到周围的状态。

同时每一种操作都有周期性限制,也即最多需要几次操作多于这个次数产生相等效果的循环。

这类题也有一个共同的解决思路:确定一个小的枚举方案,这个枚举方案产生的结果一定会促使下面的操作。例如本题中我们随机枚举1,2,3操作,如果A,B,C没有归零我们就必须操作4,5,6来让其归零(此时只有4,5,6可以影响A,B,C)。最后我们再来操作7,9能否让D,E,F归零(此时只有7,9可以影响D,E,F),最后我们再来操作8让G,H,I归零,如果可以做到,则记录下该解。

解决思路有了,前期我来这样写的话结果是写出了一个大模拟,还一直有bug。

在此写一句话:数学思路是大模拟的克星

对于很多恶心人的大模拟,其实一般都有数学规律可循,找到了数学规律也就可以避免模拟中的乱七八糟的数组,n^2复杂度。。

在网上借鉴了一位大神的思路,代码如下:

#include<cstdio>
int z[10], i[10], sum; //钟表,操作和操作步数
int main() {
    for(int j = 1; j <= 9; j++)
        scanf("%d", &z[j]);
    for(i[1] = 0; i[1] < 4; i[1]++)
        for(i[2] = 0; i[2] < 4; i[2]++)
            for(i[3] = 0; i[3] < 4; i[3]++) { //枚举操作1,2,3
                i[4] = (4 - (z[1] + i[1] + i[2]) % 4) % 4;
                i[5] = (4 - (z[2] + i[1] + i[2] + i[3]) % 4) % 4;
                i[6] = (4 - (z[3] + i[2] + i[3]) % 4) % 4;
                i[7] = (4 - (z[4] + i[1] + i[4] + i[5]) % 4) % 4;
                i[9] = (4 - (z[6] + i[3] + i[5] + i[6]) % 4) % 4;
                i[8] = (4 - (z[8] + i[5] + i[7] + i[9]) % 4) % 4; //8要放在最后
                sum = 0;
                sum += (z[1] + i[1] + i[2] + i[4]) % 4;
                sum += (z[2] + i[1] + i[2] + i[3] + i[5]) % 4;
                sum += (z[3] + i[2] + i[3] + i[6]) % 4;
                sum += (z[4] + i[1] + i[4] + i[5] + i[7]) % 4;
                sum += (z[5] + i[1] + i[3] + i[5] + i[7] + i[9]) % 4;
                sum += (z[6] + i[3] + i[5] + i[6] + i[9]) % 4;
                sum += (z[7] + i[4] + i[7] + i[8]) % 4;
                sum += (z[8] + i[5] + i[7] + i[8] + i[9]) % 4;
                sum += (z[9] + i[6] + i[8] + i[9]) % 4;
                if(sum == 0) {
                    for(int j = 1; j <= 9; j++)
                        while(i[j]--)
                            printf("%d ", j);
                    return 0;
                }
            }
}

猜你喜欢

转载自blog.csdn.net/a1097304791/article/details/82420319