以下是我個人對回溯的理解。
回溯,是嘗試列舉出所有解決問題的方法。
即按照問題所給的操作方法,進行模擬,當在尋找解的過程中,發現不符合題意的解,返回至上一步,如此重複,直到尋找到滿足問題的解的過程。
由於回溯過程會產生解空間樹,可以將求解過程,看作是對樹的遍歷操作。在遍歷的同時,判斷是否在當前結點處繼續往子樹遍歷的過程。
接着是對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;
}
對於一種思想的描述,我現在只能做到這裏了(我也很絕望呀)。