回溯(Backtracking)

以下是我個人對回溯的理解。
回溯,是嘗試列舉出所有解決問題的方法。
即按照問題所給的操作方法,進行模擬,當在尋找解的過程中,發現不符合題意的解,返回至上一步,如此重複,直到尋找到滿足問題的解的過程。
由於回溯過程會產生解空間樹,可以將求解過程,看作是對樹的遍歷操作。在遍歷的同時,判斷是否在當前結點處繼續往子樹遍歷的過程。

接着是對ZOJ上的代碼的整理:

/*
 *ZOJ 1002
 *judge status: Accepted
 *language: C++
 *run time(ms): 0
 *rum Memory(kb): 276
*/
#include <iostream>
using namespace std;
const int MAXN = 4;//矩陣的維度
int n;
char Map[MAXN][MAXN];//存儲題目中的數據
bool isPass[MAXN][MAXN];//標記矩陣中的位置是否經過
void Search(int i, int j, int &Max, int cnt);
bool isInside(int i, int j);
bool isSafe(int row, int col);
int main()
{
    std::ios_base::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n && n) {
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < n; ++j)
                cin>>Map[i][j], isPass[i][j] = false;
        int Max = 0;
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < n; ++j)
                Search(i, j, Max, 0);
        cout<<Max<<endl;
    }
    return 0;
}
/*以下對Search方法進行介紹:
 *其中的Max是所求的解,即最大值;cnt是在當前求解過程中記錄的當前最大值;
 *其中的isSafe方法是判斷繼續遍歷子樹的條件是否滿足;
 *該方法的編寫思路是:
 *按照題目所給的要求,
 *1)在'.'處可放置Gun,則嘗試放入Gun,並用isSafe方法判斷是否可以放置;
 *2)無論是否可以放置當前的Gun,都在當前狀態下,往四周嘗試放置一把Gun;
 *3)將以上對當前狀態的處理還原,避免對之後操作的影響
 *4)並返回上一狀態。
 *
 *
 *
*/
void Search(int i, int j, int &Max, int cnt)
{
    isPass[i][j] = true;
    if(Map[i][j] == '.' && isSafe(i, j)) {
        Map[i][j] = 'G';
        cnt++;
        Max = Max > cnt? Max: cnt;
    }
    if(!isPass[i][j + 1] && isInside(i, j + 1)) {
        Search(i, j + 1, Max, cnt);
    }
    if(!isPass[i + 1][j] && isInside(i + 1, j)) {
        Search(i + 1, j, Max, cnt);
    }
    if(!isPass[i][j - 1] && isInside(i, j - 1)) {
        Search(i, j - 1, Max, cnt);
    }
    if(!isPass[i - 1][j] && isInside(i - 1, j)) {
        Search(i - 1, j, Max, cnt);
    }
    if(Map[i][j] == 'G') {
        Map[i][j] = '.';
        cnt--;
    }
    isPass[i][j] = false;
}
bool isInside(int i, int j)
{
    if(i < 0 || i >= n) return false;
    if(j < 0 || j >= n) return false;
    return true;
}
bool isSafe(int row, int col)
{
    bool rowOk = true, colOk = true;
    for(int i = 1; row - i >= 0 || row + i < n; ++i)
    {
        if(row - i >= 0) {
            if(Map[row - i][col] == 'G') {
                rowOk = false;
                break;
            }
            if(Map[row - i][col] == 'X') break;
        }
        if(row + i < n) {
            if(Map[row + i][col] == 'G') {
                rowOk = false;
                break;
            }
            if(Map[row + i][col] == 'X') break;
        }
    }
    for(int i = 1; col - i >= 0 || col + i < n; ++i)
    {
        if(col - i >= 0) {
            if(Map[row][col - i] == 'G') {
                colOk = false;
                break;
            }
            if(Map[row][col - i] == 'X') break;
        }
        if(col + i < n) {
            if(Map[row][col + i] == 'G') {
                colOk = false;
                break;
            }
            if(Map[row][col + i] == 'X') break;
        }
    }
    return rowOk && colOk;
}
/*
 *ZOJ 1003
 *參考博客:
 *http://blog.csdn.net/hackdevil/article/details/9190359
*/
/*
 *judge status: Accepted
 *language: C++
 *run time(ms): 10
 *run memory(kb): 276
*/
#include <iostream>
using namespace std;
void whichOne(int n, int m, bool &AWin, bool &BWin, int div);
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int n, m;
    while(cin>>n>>m) {
        if(n < m) {//exchange value
            n = n^m;
            m = n^m;
            n = n^m;
        }
        bool AWin = false, BWin = false;
        whichOne(n, m, AWin, BWin, 2);
        if(!AWin && BWin) cout<<m<<endl;
        else cout<<n<<endl;
    }
    return 0;
}
/*以下對whichOne方法進行介紹:
 *其中的div是n,m的分解因子
 *題目要求可以理解爲,n分解得到的任意一個分解因子都不包含在m的分解因子中;
 *也就是需要將所有的分解等式列舉出來,這通過回溯實現。
 *1)如果兩者都能分解,則A勝,否則只要B能分解,則B勝;
 *2)for循環就是嘗試對n, m用div進行分解,並將分解的結果,繼續分解;
 *3)之後用div+1繼續嘗試分解,直到超過分解範圍
 *4)並返回上一步
 *
 *
 *
*/
void whichOne(int n, int m, bool &AWin, bool &BWin, int div)
{
    if(n == 1 && m == 1) {
        AWin = true;
        return;
    }
    if(m == 1) BWin = true;
    for(int x = div; (n >= x || m >= x) && x < 101; ++x) {
        if(m % x == 0) {
            whichOne(n, m / x, AWin, BWin, x + 1);
            if(AWin) return;
        }
        if(n % x == 0) {
            whichOne(n / x, m, AWin, BWin, x + 1);
            if(AWin) return;
        }
    }
}
/*
 *ZOJ 1004
 *judge status: Accepted
 *language: C++
 *run time(ms): 0
 *run memory(kb): 276
*/
#include <iostream>
#include <string>
#include <iterator>
#include <stack>
#include <vector>
using namespace std;
string s1, s2;
stack<char> s;
vector<char> vOfSta;
void backtrack(int subscr1, int subsrc2, int len);
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>s1>>s2) {
        int len = s1.size();
        cout<<'['<<endl;
        backtrack(0, 0, len);
        cout<<']'<<endl;
        vOfSta.clear();
    }
    return 0;
}
/*以下對backtrack方法的進行介紹:
 *subscr1是對字符串1下標的表示;subscr2同理;
 *該backtrack方法是,對棧進棧出棧的模擬,即模擬棧對一個元素的進棧或出棧的操作;
 *1)當字符串下標小於字符長度時,先將字符串push到棧中,並繼續調用該方法;
 *2)當字符串全部壓入棧中後,嘗試pop元素,如果符合條件,則輸出結果;
 *3)並將棧還原到原來的狀態;
 *4)如果字符串下標大於等於字符長度時,繼續彈出多餘的字符;
 *5)將當前的字符嘗試彈出,並調用backtrack方法;
 *6)如果字符彈出,則將棧的狀態進行還原;
 *7)彈出進棧的元素並返回上一步;
 *以下注釋的backtrack方法,只是一個草稿。
 *
 *
 *
*/
void backtrack(int subsrc1, int subsrc2, int len)
{
    int index = subsrc2;
    if(subsrc1 < len) {
        s.push(s1[subsrc1]);
        vOfSta.push_back('i');
        backtrack(subsrc1 + 1, subsrc2, len);
    } else {
        while(!s.empty()) {
            if(s.top() != s2[subsrc2]) break;
            s.pop();
            vOfSta.push_back('o');
            subsrc2++;
        }
        if(s.empty()) {
            vector<char>::iterator itOfS = vOfSta.begin();
            vector<char>::iterator itOfE = vOfSta.end();
            for( ; itOfS != itOfE; ++itOfS) cout<<*itOfS<<' ';
            cout<<endl;
        }
        while(index < subsrc2) {
            s.push(s2[subsrc2 - 1]);
            vOfSta.pop_back();
            subsrc2--;
        }
        return ;
    }
    if(subsrc1 + 1 == len) {
        s.pop();
        vOfSta.pop_back();
        return ;
    }
    while(!s.empty() && s.top() == s2[subsrc2]) {
        s.pop();
        vOfSta.push_back('o');
        backtrack(subsrc1 + 1, subsrc2 + 1, len);
        subsrc2++;
    }
    while(index < subsrc2) {
        s.push(s2[subsrc2 - 1]);
        vOfSta.pop_back();
        subsrc2--;
    }
    s.pop();
    vOfSta.pop_back();
}
/*
void backtrack(int subsrc, int len)
{
    int size = s.size();
    if(subsrc < len) {
        s.push(s1[subsrc]);
        vOfSta.push_back('i');
        backtrack(subsrc + 1, len);
    } else {
        for(int i = 0; i < (int)vOfSta.size(); ++i) cout<<vOfSta[i]<<'_';
        for(int i= 0; i < size; ++i) cout<<"o_";
        cout<<endl;
        return ;
    }
    if(subsrc + 1 == len) {
        s.pop();
        vOfSta.pop_back();
        return ;
    }
    stack<char> tmp;
    while(!s.empty()) {
        tmp.push(s.top());
        s.pop();
        vOfSta.push_back('o');
        backtrack(subsrc + 1, len);
    }
    while(!tmp.empty()) {
        s.push(tmp.top());
        tmp.pop();
        vOfSta.pop_back();
    }
    s.pop();
    vOfSta.pop_back();
}
*/
/*
 *ZOJ 1005
 *judge status: Accepted
 *language: C++
 *run time(ms): 10
 *rum Memory(kb): 1388
*/
#include <iostream>
#include <stack>
#include <string>
using namespace std;
const int MAXN = 1002;
bool status[MAXN][MAXN];
stack<string> ans;
bool backtrack(int cA, int cB, int wA, int wB, int n);
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int cA, cB, n;
    for(int i = 0; i < MAXN; ++i)
        for(int j = 0; j < MAXN; ++j)
            status[i][j] = false;
    while(cin>>cA>>cB>>n) {
        if(backtrack(cA, cB, 0, 0, n)){
            while(!ans.empty()) {
                cout<<ans.top()<<endl;
                ans.pop();
            }
        }
    }
    return 0;
}
/*以下是對backtrack方法的介紹:
 *其中的cA, cB, wA, wB分別表示Jug A的容量,Jug B的容量,Jug A的水量,Jug B的水量;
 *其中的status是標記是否遍歷過
 *1)分別對題中fill A、fill B、empty A、empty B、pour A B、pour B A進行模擬
 *2)還原原來的狀態
 *
 *
 *
*/
bool backtrack(int cA, int cB, int wA, int wB, int n)
{
    if(wA == 0 && wB == n) {
        ans.push("success");
        return true;
    }
    if(status[wA][wB]) return false;

    status[wA][wB] = true;
    if(cA > 0) {
        if(backtrack(0, cB, wA + cA, wB, n)) {
            ans.push("fill A"), status[wA][wB] = false;
            return true;
        }
    }
    if(cB > 0) {
        if(backtrack(cA, 0, wA, wB + cB, n)) {
            ans.push("fill B"), status[wA][wB] = false;
            return true;
        }
    }
    if(wA > 0) {
        if(backtrack(cA + wA, cB, 0, wB, n)) {
            ans.push("empty A"), status[wA][wB] = false;
            return true;
        }
    }
    if(wB > 0) {
        if(backtrack(cA, cB + wB, wA, 0, n)) {
            ans.push("empty B"), status[wA][wB] = false;
            return true;
        }
    }
    if(wA > cB) {
        if(backtrack(cA + cB, 0, wA - cB, wB + cB, n)) {
            ans.push("pour A B"), status[wA][wB] = false;
            return true;
        }
    }
    else if(wA <= cB) {
        if(backtrack(cA + wA, cB - wA, 0, wB + wA, n)) {
            ans.push("pour A B"), status[wA][wB] = false;
            return true;
        }
    }
    if(wB > cA) {
        if(backtrack(0, cB + cA, wA + cA, wB - cA, n)) {
            ans.push("pour B A"), status[wA][wB] = false;
            return true;
        }
    }
    else if(wB <= cA) {
        if(backtrack(cA - wB, cB + wB, wA + wB, 0, n)) {
            ans.push("pour B A"), status[wA][wB] = false;
            return true;
        }
    }
    status[wA][wB] = false;
    return false;
}

對於一種思想的描述,我現在只能做到這裏了(我也很絕望呀)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章