UVa140(带宽)

这道题在写之前一定要把题目读懂,笔者在设计代码时就对题意产生了错误的理解,好在后来这个错误被纠正了。

这道题最主要的点就是对解答树遍历并且回溯,也就是《算法竞赛入门经典》中所提到的“剪枝”。递归的主体是生成结点的全排列,而回溯操作简单来说就是在这个递归的基础上添加的一个判断。

先说生成全排列递归的操作,生成全排列就是在现有数组的基础上对数组进行新元素的插入,每次插入都会对已有数组进行遍历,如果发现新插入的元素在前面的数组中已经出现过,那么就跳过剩余操作插入下一个元素,递归的结束条件就是当前数组的长度与给出数组的长度相同。

再说回溯操作,程序中添加了一个变量MINB来记录当前所有排列中最小的带宽,每当数组中新添加了一个元素,就计算该元素(结点)与它相邻的结点的距离,如果此距离超过了最小带宽,那么就不用再遍历下去了,直接回溯。

需要提到的是,程序中图是使用邻接矩阵存储的,不过在把代码写完后,笔者发现或许使用邻接表存储会更加方便。

  1 #include <bits/stdc++.h>
  2 
  3 using namespace std;
  4 
  5 /**************** 
  6 UVa140(带宽) 
  7 ***************/
  8 
  9 const int MAX = 8; 
 10  
 11 int node[MAX]; 
 12 //用来记录带宽最小的结点序列  
 13 int minB[MAX]; 
 14 //邻接矩阵  
 15 int buf[MAX][MAX];
 16 int MAXL = 0;//带宽 
 17 int MINB = 99999;//最小带宽  
 18 
 19 //该函数是求排列的带宽的  
 20 //n:结点个数(字符数组长度)
 21 //cur:当前位置  
 22 int BandWidth(int n){
 23     int cur = 0, i = 0, bnw = 0,j;
 24     while(i < n && cur < n){
 25         int k = node[cur];
 26         //寻找相邻结点 
 27         if(buf[k][i]){
 28             for(j = 0; j < n; j++){
 29                 //在结点序列中寻找相邻结点  
 30                 if(node[j] == i){
 31                     bnw = max(abs(j-cur), bnw);
 32                 }
 33             }
 34         }
 35         //某个结点遍历结束 
 36         if(i == n-1){
 37             i = 0;
 38             cur++; 
 39             continue;
 40         }
 41         i++; 
 42     }
 43     return bnw;
 44 }
 45 
 46 void solve(int n, int cur)
 47 {
 48     if(cur == n){
 49         MAXL = BandWidth(n);
 50         if(MINB > MAXL){
 51             for(int i = 0; i < n; i++){
 52                 minB[i] = node[i];
 53             }
 54             MINB = MAXL;
 55         }
 56     }
 57     else{
 58         for(int i = 0; i < n; i++){
 59             int ok = 1;
 60             for(int j = 0; j < cur; j++)
 61                 if(node[j] == i) ok = 0;
 62             if(ok){
 63                 int tmp = 0;
 64                 //寻找相邻结点  
 65                 for(int j = 0; j < n; j++){
 66                     if(buf[i][j]){
 67                         //遍历当前数组有没有相邻结点  
 68                         for(int k = 0; k < cur; k++)
 69                             if(node[k] == j)
 70                                 tmp = MINB - (cur - k); 
 71                     }
 72                     if(tmp < 0) return;
 73                 }
 74                 node[cur] = i;
 75                 solve(n, cur+1); 
 76             }
 77         }
 78     }
 79     
 80 }
 81 
 82 int main()
 83 {
 84     //n:结点数  
 85     int n = 0;
 86     cin >> n; 
 87     //x,y是边的两条结点  
 88     char x, y;
 89     memset(buf, 0, sizeof(buf)); 
 90     //构建邻接矩阵   
 91     while(true){
 92         cin >> x;
 93         cin >> y;
 94         if(x == '#' && y == '#') break;
 95         buf[x-65][y-65] = 1;
 96         buf[y-65][x-65] = 1;
 97     } 
 98     solve(n, 0);
 99     //输出结果  
100     for(int i = 0; i < n; i++){
101         printf("%c", minB[i]+65);
102     }
103     printf("\n");
104     printf("%d", MINB); 
105     return 0;
106 } 

在编写代码时觉得代码还不够精简,或许还有优化空间。

运行结果为:

8
A B
A F
B C
B G
C D
D E
D G
E H
F G
F H
# #
ABCFGDHE
3

 

posted @ 2020-08-16 17:51  江南湖西北  阅读(291)  评论(0编辑  收藏  举报