在回溯法中,每个决策对应于给一个节点产生新的子树,而解的生成过程对应一颗解答树,节点的层数就是下一个待填充位置$cur$。
在多阶段决策的最优化问题,大多可以用dp解决,状态转移则类似于回溯法中的解答树。
UVa 116
$d(i,j)$表示从第此处出发到达最后一列的最小和,要求字典序最小,就同时要记录行号的最小值。
代码:
1 // 2 // main.cpp 3 // UVa 116 4 // 5 // Created by Yanbin GONG on 12/3/2018. 6 // Copyright ? 2018 Yanbin GONG. All rights reserved. 7 // 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string> 12 #include <string.h> 13 #include <algorithm> 14 #include <cmath> 15 16 using namespace std; 17 18 int m,n; 19 int cell[15][105]; 20 int d[15][105]; 21 int path[15][105]; //记录从该处应该走到的下一个点 22 int tmp; 23 int ans; 24 int startNode; 25 //int updown[3] = {-1,0,1}; 写在dp中,方便改变状态,满足在上下边时候的移动 26 27 28 int main(){ 29 while(cin>>n>>m){ 30 memset(d,0x3f,sizeof(d)); 31 ans = 0x3f3f3f3f; 32 for(int i=1;i<=n;i++){ 33 for(int j=1;j<=m;j++){ 34 cin>>cell[i][j]; 35 } 36 d[i][m] = cell[i][m];//初始化边界 37 } 38 //dp 39 for(int j=m;j>1;j--){ 40 41 for(int i=1;i<=n;i++){ 42 int updown[3] = {i-1,i,i+1}; //表示接下来处在的行 43 if(i==1) updown[0] = n; 44 if(i==n) updown[2] = 1; 45 //排序找到最小字典序的核心代码!因为这个WA了 46 sort(updown,updown+3); //排序找到最小字典序的核心代码!因为这个WA了 47 //排序找到最小字典序的核心代码!因为这个WA了 48 for(int k=0;k<3;k++){ 49 tmp = d[i][j] + cell[updown[k]][j-1]; //到[][j-1]的总成本 50 if(tmp<d[updown[k]][j-1]){ 51 d[updown[k]][j-1] = tmp; 52 path[updown[k]][j-1] = i;//反向记录从[][j-1]走到[i][j] 53 } 54 } 55 } 56 } 57 //找到最优解的开始 58 for(int i=1;i<=n;i++){ 59 if(ans>d[i][1]){ 60 ans = d[i][1]; 61 startNode = i; 62 } 63 } 64 //print 65 printf("%d",startNode); 66 int nextNode = path[startNode][1]; 67 for(int j=1;j<=m;j++){ 68 printf(" %d",nextNode); 69 nextNode = path[nextNode][j]; 70 } 71 printf("\n%d\n",ans); 72 } 73 return 0; 74 }
这题要注意将$updown$更新后重新排序一遍,这样才能确定找到的字典序是最小的