(BruteForce)暴力破解經典題目總結

 在算法競賽中,不少問題是來不及用數學公式推導出來的。或者說根本就找不到數學規律,這時咱們就須要使用枚舉來暴力破解。ios

不過枚舉也是須要腦子的,一味的暴力只能超時。所以我這裏選擇了幾道mooc上經典的題目來作複習。c++

一、完美立方。

 

 

 

  思路:算法

  從2到N枚舉a的值,2到a枚舉d的值,2到d的枚舉b的值,2到c枚舉b的值。當數組

  知足a*a*a==b*b*b+c*c*c+d*d*d
  的時候對結果輸出。
  代碼以下:
#include <iostream>

using namespace std;
int main() {
    int N;
    cin>>N;
    for(int a=2;a<=N;a++){
        for(int d=2;d<=a;d++){
            for(int c=2;c<=d;c++){
                for(int b=2;b<=c;b++){
                    if(a*a*a==b*b*b+c*c*c+d*d*d) cout<<"Cube = "<<a<<", Thiple = ("<<b<<","<<c<<","<<d<<")"<<endl;
                }
            }
        }
    }
    return 0;
}

運行結果以下:spa

 

二、生理週期

 

 

 

 

 

作此題咱們首先會有個思路就是從d+1開始一個一個嘗試,直至知足條件(
(k-p)%23==0&&(k-e)%28==0&&(k-i)%33==0)
因此咱們很輕易地就能寫出對應的代碼。代碼以下:
#include <iostream>
using namespace std;
int main(){
    int p,e,i,d,N=0;
    while(cin >> p >> e >> i >> d&&p!=-1) {
        int k;
        for(k=d+1;(k-p)%23||(k-e)%28||(k-i)%33;k++);
        cout<<"Case "<<++N<<": The next triple peak occurs in "<<k-d<<" days."<<endl;
    }
    return 0;
}

 

不過在此咱們是否會有個疑問。一個一個的嘗試是否過於浪費時間了?
這裏咱們給出另外一種思路。
咱們先枚舉出k+1後,第一個知足高峯的時間點做爲第一種高峯的基數,這時咱們須要等到k-p%23==0的時候中止。
這時的k表明着知足了k-p。
而後咱們枚舉下一種高峯的時間基數。此次咱們每次只須要+23就夠了。由於23之內確定不知足第一種高峯。
隨後咱們再枚舉第三個知足高峯的時間點的基數,這個基數-d就是咱們最終的答案,此次咱們每次跳過23*28個單位便可。
整體代碼以下所示。
#include <iostream>
using namespace std;
int main(){
    int p,e,i,d,N=0;
    while(cin >> p >> e >> i >> d&&p!=-1) {
        int k;
        for(k=d+1;(k-p)%23;k++);//c++中正數就是true
       for(;(k-e)%28;k+=23);
        for(;(k-i)%33;k+=23*28);
        cout<<"Case "<<++N<<": The next triple peak occurs in "<<k-d<<" days."<<endl;
    }
    return 0;
}


運行結果以下。

三、POJ1013 Counterfeit Dollar(mooc中稱硬幣)

 題目連接3d

 這題目不難,可是這道題我WA了不少次。其中我說下WA的緣由:

1.每一個盤子稱的牌子不必定有4個。code

2.若是某個假幣可能輕可能重,不要continue,要以heavy----light的倆次形式輸出。blog

 

思路:ip

定義一個大小爲9字符串數組。做爲每次輸入的字符串。ci

依次假設每一個硬幣爲假的。而後從重到輕挨個枚舉,當找到假幣的時候輸出。

下面是AC代碼:

#include <iostream>
#include <string>
#include <map>

using namespace std;
int main(){
    int N;
    cin>>N;
    map<char,int> m;
    for(int i=65;i<77;i++) {
        m[(char)i]=0;
    }
    while(N--){
        string str[9];
        cin>>str[0]>>str[1]>>str[2]>>str[3]>>str[4]>>str[5]>>str[6]>>str[7]>>str[8];
        for(int i=65;i<77;i++){
            int sign = 0;
            int left = 0,right = 0;
            string res="";
            m[(char)i]=1;
            for(int k=0;k<3;k++) {
                for (int j = 0; j < str[0+3*k].length(); j++)left += m[str[0+3*k][j]];
                for (int j = 0; j < str[1+3*k].length(); j++)right += m[str[1+3*k][j]];
                if (left == right) res = "even";
                if (left > right) res = "up";
                if (left < right) res = "down";
                if (res == str[2+3*k])sign++;
                left=0;
                right=0;
            }
            if(sign==3)cout<<(char)i<<" is the counterfeit coin and it is heavy.\n";

            sign=0;
            res="";
            m[(char)i]=-1;
            for(int k=0;k<3;k++) {
                for (int j = 0; j < str[0+3*k].length(); j++)left += m[str[0+3*k][j]];
                for (int j = 0; j < str[1+3*k].length(); j++)right += m[str[1+3*k][j]];
                if (left == right) res = "even";
                if (left > right) res = "up";
                if (left < right) res = "down";
                if (res == str[2+3*k])sign++;
                left=0;
                right=0;
            }
            if(sign==3) cout<<(char)i<<" is the counterfeit coin and it is light.\n";

            m[(char)i]=0;
        }
    }
    return 0;
}

 

 

四、EXTENDED LIGHTS OUT(POJ 1222)

題目連接

這道題着實有點難。不過咱們此次用枚舉的方法解答這個問題。

分析:

輸入一個5*6的矩陣,讓咱們從中選取一個按鈕方案,使最終全部燈都熄滅。

思路:

0、目標使全部燈的狀態變爲0。

一、想不干擾第1行別的燈的狀態來改變第1行,就須要按下面的按鈕才能準確的改變。

二、別的行數也是以此類推。

三、這樣別的行數的狀態都會隨着第一行的變化而肯定。

AC代碼以下:

#include <iostream>
using namespace std;
int Matrix[5][6];
int res[5][6];
int temp[5][6];
void Flip(int &a){
    if(a) a=0;
    else a=1;
}
bool Check(){
    for(int i=0;i<6;i++){
        if(temp[4][i]) return false;
    }
    return true;
}
void print(int t){
    cout<<"PUZZLE #"<<t<<endl;
    for(int i=0;i<5;i++){
        for(int j=0;j<6;j++) {
            if(j==5)cout<<res[i][j];
            else cout << res[i][j] << " ";
        }
        cout<<endl;
    }
}
int main(){
    int N;cin>>N;
    for(int t=1;t<=N;t++){
        for(int i=0;i<5;i++)for(int j=0;j<6;j++)cin>>Matrix[i][j];//輸入矩陣
        for(int a=0;a<2;a++)for(int b=0;b<2;b++)for(int c=0;c<2;c++)for(int d=0;d<2;d++)for(int e=0;e<2;e++)for(int f=0;f<2;f++){
            int A[6] = {a,b,c,d,e,f};
             for(int i=0;i<5;i++){
               for(int j=0;j<6;j++) res[i][j]=0;
             }
            for(int i=0;i<5;i++) {
                for(int j=0;j<6;j++){
                    temp[i][j]=Matrix[i][j];
                }//循環copy
            }
            for(int i=0;i<6;i++){//依照枚舉數組將第一排按下
                if(A[i]==1){
                    res[0][i]=1;
                    switch (i){
                        case 0:
                            Flip(temp[0][i]);
                            Flip(temp[0][i+1]);
                            Flip(temp[1][i]);
                            break;
                        case 5:
                            Flip(temp[0][i]);
                            Flip(temp[0][i-1]);
                            Flip(temp[1][i]);
                            break;
                        default:
                            Flip(temp[0][i]);
                            Flip(temp[0][i-1]);
                            Flip(temp[0][i+1]);
                            Flip(temp[1][i]);
                            break;
                    }
                }
            }
            for(int i=0;i<3;i++){//其餘數組依次往下按
                for(int j=0;j<6;j++){
                    if(temp[i][j]==1){
                        res[i+1][j]=1;
                        switch (j){
                            case 0:
                                Flip(temp[i][j]);
                                Flip(temp[i+1][j+1]);
                                Flip(temp[i+1][j]);
                                Flip(temp[i+2][j]);
                                break;
                            case 5:
                                Flip(temp[i][j]);
                                Flip(temp[i+1][j-1]);
                                Flip(temp[i+1][j]);
                                Flip(temp[i+2][j]);
                                break;
                            default:
                                Flip(temp[i][j]);
                                Flip(temp[i+1][j-1]);
                                Flip(temp[i+1][j]);
                                Flip(temp[i+1][j+1]);
                                Flip(temp[i+2][j]);
                                break;
                        }
                    }
                }
            }
            for(int i=0;i<6;i++){
                if(temp[3][i]==1){
                    res[4][i]=1;
                    switch (i){
                        case 0:
                            Flip(temp[3][i]);
                            Flip(temp[4][i+1]);
                            Flip(temp[4][i]);
                            break;
                        case 5:
                            Flip(temp[3][i]);
                            Flip(temp[4][i-1]);
                            Flip(temp[4][i]);
                            break;
                        default:
                            Flip(temp[3][i]);
                            Flip(temp[4][i-1]);
                            Flip(temp[4][i+1]);
                            Flip(temp[4][i]);
                            break;
                    }
                }
            }
            if(Check()){
                print(t);
            }
        }
    }
}

不過這題能夠利用二進制枚舉法進行簡化。

什麼是二進制枚舉法呢?

我在枚舉從000000~111111的時候,我嵌套了6層循環,若是使用二進制來表示這個數組,我只須要枚舉從0到31便可。

下面就是mooc老師寫的代碼。

#include <memory>
#include <string>
#include <cstring>
#include <iostream>
using namespace std;
int GetBit(char c,int i) {
//取c的第i位
    return ( c >> i ) & 1;
}
void SetBit(char & c,int i, int v) {
//設置c的第i位爲v
    if( v )
        c |= ( 1 << i);
    else
        c &= ~( 1 << i);
}
void Flip(char & c, int i) {
//將c的第i位爲取反
    c ^= ( 1 << i);
}
void OutputResult(int t,char result[]) //輸出結果
{
    cout << "PUZZLE #" << t << endl;
    for( int i = 0;i < 5; ++i ) {
        for( int j = 0; j < 6; ++j ) {
            cout << GetBit(result[i],j);
            if( j < 5 )
                cout << " ";
        }
        cout << endl;
    }
}
int main() {
    char oriLights[5]; //最初燈矩陣,一個比特表示一盞燈
    char lights[5]; //不停變化的燈矩陣
    char result[5]; //結果開關矩陣
    char switchs; //某一行的開關狀態
    int T;
    cin >> T;
    for( int t = 1; t <= T; ++ t) {
        memset(oriLights,0,sizeof(oriLights));
        for( int i = 0;i < 5; i ++ ) { //讀入最初燈狀態
            for( int j = 0; j < 6; j ++ ) {
                int s;
                cin >> s;
                SetBit(oriLights[i],j,s);
            }
        }
        for( int n = 0; n < 64; ++n ) { //遍歷首行開關的64種狀態
            memcpy(lights,oriLights,sizeof(oriLights));
            switchs = n; //第i行的開關狀態
            for( int i = 0;i < 5; ++i ) {
                result[i] = switchs; //第i行的開關方案
                for( int j = 0; j < 6; ++j ) {
                    if( GetBit(switchs,j)) {
                        if( j > 0)
                            Flip(lights[i],j-1);//改左燈
                        Flip(lights[i],j);//改開關位置的燈
                        if( j < 5 )
                            Flip(lights[i],j+1);//改右燈
                    }
                }
                if( i < 4 )
                    lights[i+1] ^= switchs;//改下一行的燈
                switchs = lights[i]; //第i+1行開關方案和第i行燈狀況同
            }
            if( lights[4] == 0 ) {
                OutputResult(t,result);
                break;
            }
        } // for( int n = 0; n < 64; n ++ )
    }
    return 0;
}

PS:這手二進制枚舉秀到我了。

 總結:暴力破解也不是很輕鬆。須要使用者對破解邊界有足夠的熟練度才行。

相關文章
相關標籤/搜索