1. 程式人生 > >撥鍾問題 OpenJ_Bailian

撥鍾問題 OpenJ_Bailian

這是一道和畫家問題,熄燈問題極為相似的題目。

共同特性:操作對環境的改變是無序的,每個操作都會影響到周圍的狀態。

同時每一種操作都有周期性限制,也即最多需要幾次操作多於這個次數產生相等效果的迴圈。

這類題也有一個共同的解決思路:確定一個小的列舉方案,這個列舉方案產生的結果一定會促使下面的操作。例如本題中我們隨機列舉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;
                }
            }
}