回溯法(Backtrack)
the 3-coloring problem(3着色问题)
Given an undirected graph G=(V,E), it is required to color each vertex in V with one of three colors, say 1,2, and 3, such that no two adjacent vertices have the same color.
search tree(搜索树)
首先抛弃数据结构中所学到的树以及二叉搜索树等概念,在算法中的搜索树的节点常常用来表示某个处理阶段所处的状态。以3着色问题为例,讲解搜索树。
如图中的无向图所示,现需要用3个颜色对顶点进行着色,但要求相邻节点的颜色不一样,那么该问题的所有可能的解可以用一个搜索树来表示,如下
其中根节点代表初始状态,没有颜色填充,第二行节点代表第一个节点被填充的颜色的所有可能,第三行表示第二个节点被填充的所有颜色,不管是否符合要求,都要一一列举出来。
显然这类求解方式的时间复杂度为指数级别的,3着色问题的时间复杂度为
O
(
3
n
)
O(3^n)
O(3n)
Backtrack(回溯法)
接下来进入正题部分,回溯法为了避免搜索树将所有可能性全部列出来而残生的,根据要求及时检查,符合要求继续深度搜索,反之回溯。
具体的步骤如下:
1.如果搜索树没有搜索到底部(或者定义搜索树深度为n,搜索深度小于n),并且当前节点符合要求,那么继续向下搜索。
2.如果搜索深度小于n,当前节点不符合要求,回溯到上一个节点搜索。
3.如果搜索深度==n并且当前节点符合要求,那么找到正确的解并输出。
4.如果搜索完所有节点都没有成功,那么搜索失败,问题无解。
针对3着色问题,原版论述如下:
c++代码实现
存在回溯过程,复杂度变为 O ( n 3 n ) O(n3^n) O(n3n),但是实际效果却比搜索树中 O ( 3 n ) O(3^n) O(3n)要好得多。
#include<iostream>
using namespace std;
#define colornum 3
#define vecnum 5
bool flag;
//检查添加的颜色是否符合要求
bool check(int g[][5],int* color,int k){
for(int j=0;j<vecnum;j++){
if(g[k][j]==1&&color[j]==color[k]){
return false;
}
}
return true;
}
void dfs(int g[][5],int* color,int k){
if(k==vecnum){//搜索完最后一个节点,输出结果%节点号:颜色
for(int i=0;i<vecnum;i++)
cout<<i<<":"<<color[i]<<endl;
flag=true;
return;
}
for(int i=1;i<=colornum;i++){
color[k]=i;
if(check(g,color,k)&&k<vecnum){
dfs(g,color,k+1);
if(flag)return;//发现结构及时return
}
}
return;
}
int main(){
//图结构
int g[vecnum][vecnum]={0,1,1,0,0,1,0,0,1,1,1,0,0,1,1,0,1,1,0,1,0,1,1,1,0};
//存储节点颜色1,2,3
int color[vecnum]={0};
flag=false;;
dfs(g,color,0);
if(flag==false)
cout<<"cannot find result\n";//搜索失败
}