/* 货郎担问题: 四个顶点的货郎担问题。求从顶点1出发, 最后回到顶点1的最短路线。 v1 v2 v3 v4 v1 无穷 无穷 1 7 v2 8 无穷 5 1 v3 7 2 无穷 1 v4 2 5 3 无穷 算法分析: 因为是采用回溯法来做,肯定是递归,然后还需要现场清理。 要设置一个二维数组来标识矩阵内容,然后回溯还需要设计 一个二维标记数组来剪枝,设定一个目标变量,初始为无穷大, 后续如果有比目标变量值小的就更新。剪枝的条件就是如果走到当前节点的耗费值>=目标变量,就直接不再往下面走, 向上走。 深度优先 = 递归 递归基:如果到达叶子节点的上一个节点,那么就进行是否更新的判断 递归步:如果没有到达叶子节点,就进行剪枝操作,判断能否进入下一个节点,如果能,更新最优值 输入: 4 0 0 1 7 8 0 5 1 7 2 0 1 2 5 3 0 输出: 1 3 2 4 1 6 */ /* 关键: 1 //递归基:如果已经遍历到叶子节点的上一层节点,i标识递归深度 if(i == g_n) { //判断累加和是否超过最大值,如果有0,应该排除;满足这个条件,才打印 if((g_iArr[pArr[i-1]][pArr[i]] != 0) && (g_iArr[pArr[g_n]][1] != 0) && (g_iCurResult + g_iArr[pArr[i-1]][pArr[i]] + g_iArr[pArr[g_n]][1] < g_iResult )) { g_iResult = g_iCurResult + g_iArr[pArr[i-1]][pArr[i]] + g_iArr[pArr[g_n]][1]; //用当前最优路径去更新最优路径,防止下一次没有 for(int k = 1 ; k <= g_n ; k++) { g_iBestPath[k] = pArr[k]; 2 //递归步:判断能否进入子树,需要尝试每一个节点 else { //尝试不同的组合 for(int j = i ; j <= g_n ; j++) { //判断能否进入子树:如果当前值+下一个连线值的和 < 最优值,就进入,0要pass if( (g_iArr[pArr[i-1]][pArr[j]] != 0) && (g_iCurResult + g_iArr[ pArr[i-1] ][ pArr[j] ] < g_iResult) ) 3 //交换i与j,则i为当前可以尝试的范围 //为完成后面k个元素的排列,逐一对数组第n-k~n个元素互换。数组第一个元素为1,生成后面n-1个元素的排列 //数组第一个元素与第二个元素互换,第一个元素为2,第2个元素为1,生成后面的n-1个元素的排列... swap(&pArr[i],&pArr[j]); //更新当前累加值,是i-1与i的 g_iCurResult += g_iArr[ pArr[i-1] ][ pArr[i] ]; //递归 backTrace(i+1,pArr); //回溯,清空累加值;能够走到这里,说明上述结果不是最优解,需要向求解树上一层回退 g_iCurResult -= g_iArr[pArr[i-1]][ pArr[i] ]; swap(&pArr[i],&pArr[j]); */ #include <iostream> #include <stdio.h> const int MAXSIZE = 100; const int MAX = 1000000000; int g_iArr[MAXSIZE][MAXSIZE];//邻接矩阵 int g_iResult;//存放最优解 int g_iPath[MAXSIZE];//存放最优路径上 int g_n;//元素个数 int g_iCurResult;//当前累加路径和 int g_iBestPath[MAXSIZE];//还需要设置一个数组,用来保存最优解 void swap(int* pI,int* pJ) { int iTemp = *pI; *pI= *pJ; *pJ = iTemp; } void printResult(int n,int* pArr) { printf("%d\n",g_iResult); for(int i = 1 ; i <= n ; i++) { if( i != 1) { printf(" %d",pArr[i]); } else { printf("%d",pArr[i]); } } printf(" 1\n"); } //可以做成字符串全排列的性质track(int i,int* pArr,int* pResult),其中pArr是用于存放最优解的路径 void backTrace(int i,int* pArr) { //递归基:如果已经遍历到叶子节点的上一层节点 if(i == g_n) { //判断累加和是否超过最大值,如果有0,应该排除;满足这个条件,才打印 if((g_iArr[pArr[i-1]][pArr[i]] != 0) && (g_iArr[pArr[g_n]][1] != 0) && (g_iCurResult + g_iArr[pArr[i-1]][pArr[i]] + g_iArr[pArr[g_n]][1] < g_iResult )) { g_iResult = g_iCurResult + g_iArr[pArr[i-1]][pArr[i]] + g_iArr[pArr[g_n]][1]; //用当前最优路径去更新最优路径,防止下一次没有 for(int k = 1 ; k <= g_n ; k++) { g_iBestPath[k] = pArr[k]; } } } //递归步:判断能否进入子树,需要尝试每一个节点 else { //尝试不同的组合 for(int j = i ; j <= g_n ; j++) { //判断能否进入子树:如果当前值+下一个连线值的和 < 最优值,就进入,0要pass if( (g_iArr[pArr[i-1]][pArr[j]] != 0) && (g_iCurResult + g_iArr[ pArr[i-1] ][ pArr[j] ] < g_iResult) ) { //交换i与j,则i为当前可以尝试的范围 //为完成后面k个元素的排列,逐一对数组第n-k~n个元素呼唤。数组第一个元素为1,生成后面n-1个元素的排列 //数组第一个元素与第二个元素互换,第一个元素为2,第2个元素为1,生成后面的n-1个元素的排列... swap(&pArr[i],&pArr[j]); //更新当前累加值,是i-1与i的 g_iCurResult += g_iArr[ pArr[i-1] ][ pArr[i] ]; //递归 backTrace(i+1,pArr); //回溯,清空累加值;能够走到这里,说明上述结果不是最优解,需要向求解树上一层回退 g_iCurResult -= g_iArr[pArr[i-1]][ pArr[i] ]; swap(&pArr[i],&pArr[j]); } } } } void process() { //初始化 while(EOF != scanf("%d",&g_n)) { //获取输入 for(int i = 1 ; i <= g_n ; i++) { for(int j = 1 ; j <= g_n ; j++) { scanf("%d",&g_iArr[i][j]); } } //初始化根节点,最优路径初始化 for(int j = 1 ; j <= g_n ; j++) { g_iPath[j] = j; } g_iResult = MAX; g_iCurResult = 0; backTrace(2,g_iPath); printResult(g_n,g_iBestPath); } } int main(int argc,char* argv[]) { process(); getchar(); return 0; }