算法5.旅行售货员问题和数独游戏。

来源:互联网 发布:qrcode 生成算法 编辑:程序博客网 时间:2024/05/17 03:26
  1. 某售货员要到4个城市去推销商品,已知各城市之间的路程,如右图所示。请问他应该如何选定一条从城市1出发,经过每个城市一遍,最后回到城市1的路线,使得总的周游路程最小?并分析所设计算法的计算时间复杂度。
    这里写图片描述
    (1) 算法设计思路
    解向量:{1,2,3,4,1}{1,2,4,3,1,}{1,3,4,2,1}{1,3,2,4,1}{1,4,2,3,1}{1,4,3,2,1}
    解空间:是一个排序树,树的叶结点个数为(n-1)!=6
    上界函数:当前第一次所得到的值作为约束条件,然后比该值小时更新约束值
    int n,图的顶点数,int[] x 当前解,int[] bestx 当前最优解,int[][] a 图的邻接矩阵,int cc 当前费用,int bestc 当前最优值,No 无边标记
    (2) 算法实现的伪代码及其计算时间复杂度分析
    求解旅行售货员问题的算法back(int t)
    输入:t为当前城市的个数
    输出:最短路径和其对应值
s1:  If (t=n){s2: if (a[x[n-1]][x[n]]!=No&&a[x[n]][1]!=No && (cc+a[x[n-1]][n]+a[x[n]][1])<bestc || bestc=No){s3: for (inti =1 to n )best[i]=x[i]s4: bestc=cc+ a[x[n-1]][n]+a[x[n]][1]s5: }s6:  else{s7: for (inti =t to n){s8: if(a[x[t-1]][i]!=No && (cc+a[x[n-1]][n]+a[x[n]][1]<bestc || bestc =No)){s9: swap(x[t],x[i]) ;s10: cc+=a[x[t-1]][t];s11: back(t+1)s12: cc-=a[x[t-1]][t]s13: swap(x[t],x[i])s14: }s15:  }
算法fucntionA的计算时间复杂度分析:O(n!)

(3) 实验代码及运行结果

    public class TSP {        static int[] x = new int[5];        static int[] bestx = new int[5];        static int[][] a = new int[5][5];        static int cc = 0;        static int no = 10000;        static int bestc = no;        static int n = 4;        static void back(int i) {            if (i == n) {                if (a[x[n - 1]][x[n]] != 0                        && a[x[n]][1] != 0                        && (cc + a[x[n - 1]][x[n]] + a[x[n]][1] < bestc || bestc == 0)) {                    for (int j = 1; j <= n; j++)                        bestx[j] = x[j];                    bestc = cc + a[x[n - 1]][x[n]] + a[x[n]][1];                }            } else {                for (int j = i; j <= n; j++) {                    // 是否可进入x[j]子树?                    if (a[x[i - 1]][x[j]] != 0                            && (cc + a[x[i - 1]][x[i]] < bestc || bestc == 0)) {                        // 搜索子树                        // swap(x[i], x[j]);                        int temp1 = x[i];                        x[i] = x[j];                        x[j] = temp1;                        cc += a[x[i - 1]][x[i]]; // 当前费用累加                        back(i + 1); // 排列向右扩展,排列树向下一层扩展                        cc -= a[x[i - 1]][x[i]];                        // swap(x[i], x[j]);                        int temp2 = x[i];                        x[i] = x[j];                        x[j] = temp2;                    }                }            }        }        // private static void swap(int i, int j) {        // // TODO Auto-generated method stub        // int temp = i;        // i = j;        // j = temp;        // }        public static void main(String[] args) {            a[1][1] = no;            a[1][2] = 30;            a[1][3] = 6;            a[1][4] = 4;            a[2][1] = 30;            a[2][2] = no;            a[2][3] = 5;            a[2][4] = 10;            a[3][1] = 6;            a[3][2] = 5;            a[3][3] = no;            a[3][4] = 20;            a[4][1] = 4;            a[4][2] = 10;            a[4][3] = 20;            a[4][4] = no;            for (int i = 0; i <= 4; i++)                x[i] = i;            // x[1]=1;            // x[2]=3;x[3]=2;x[4]=4;            back(2);            System.out.print("最优路劲为:");            for (int i = 1; i <= 4; i++)                System.out.print(bestx[i]+"->");            System.out.println(x[1]);            System.out.println("最小值为"+bestc);        }    }

//
这里写图片描述
//

(4) 体会
用回溯算法搜索排列树的算法框架可以描述为

void back(int t){if (t>n) output(x);else for (inti =t,i<n;i++){swap(x[t],x[i]);if( contriant(t)&&bound(t)) back(t+1)swap(x[t],x[i])}}
  1. 数独游戏:九宫格是在81个格子(9×9)中,要满足以下条件:① 每个横行和竖列中的9个格子都包含数字1~9,且不重复;② 每个黑色粗实线围住的9个格子(3×3)都包含数字1~9,且不重复。如图所示:
    要求:找出给定数字的九宫格。
    输入:输入9行9列81个数字,其中0表示要填的数字。
    输出:输出满足条件的九宫格。
    这里写图片描述
    某测试样例如下:
    这里写图片描述
    (1) 算法设计思路
    数独游戏是N后问题的变形版本。将n*n看成二维矩阵,行i列j,解空间是完成n叉树。
    先判断要变化的数是否为0,如果为0,在行,列和小九宫格中判断有没有相同数字,有相同数字则+1,直到9,如果不为0,直接变化下一个数。

(2) 算法实现的伪代码及其计算时间复杂度分析
求解数独游戏的算法back(int i,int j)
输入:数组 的行i,列j
输出:满足条件的九宫格

s16:  if(i=8并且j=9){s17: 输出满足条件的九宫格s18: }s19: if (j=9){s20: j=0;i++s21: } s22:  if (x[i][j]=0)s23: for (int n=1 to 9){s24: if (判断是否在行,列,小九宫格中出现重复的值){s25: x[i][j]=n;s26: back(i,j+1)s27: x[i][j]=0;s28: }s29: else back(i,j+1)
算法fucntionB的计算时间复杂度分析:O(n^n)

(3) 实验代码及运行结果

    public class 数独游戏 {        static int[][] bestx = new int[9][9];        static int[][] x = { { 0, 6, 1, 0, 3, 0, 0, 2, 0 },                { 0, 5, 0, 0, 0, 8, 1, 0, 7 }, { 0, 0, 0, 0, 0, 7, 0, 3, 4 },                { 0, 0, 9, 0, 0, 6, 0, 7, 8 }, { 0, 0, 3, 2, 0, 9, 5, 0, 0 },                { 5, 7, 0, 3, 0, 0, 9, 0, 0 }, { 1, 9, 0, 7, 0, 0, 0, 0, 0 },                { 8, 0, 2, 4, 0, 0, 0, 6, 0 }, { 0, 4, 0, 0, 1, 0, 2, 5, 0 }, };;        static int cc = 0;        static int bestc;        static void back(int i, int j) {            if (i == 8 && j == 9) {                for (int a = 0; a <= 8; a++)                    for (int b = 0; b <=8; b++)                        bestx[a][b] = x[a][b];                return;            }            //如果j=9,则重置j,然后行+1            if (j == 9) {                j = 0;                i++;            }            if (x[i][j] == 0)                for (int n = 1; n < 10; n++) {                    if (!boo(i, j, n)) {//判断是否在行,列,小九宫格中出现重复的值                        x[i][j] = n;                        back(i, j + 1);                        x[i][j] = 0;                    }                }            else                back(i, j + 1);        }        //判断是否在行,列,小九宫格中出现重复的值        static boolean boo(int i, int j, int k) {            int m = i / 3;            int n = j / 3;            for (int a = 0; a < 9; a++) {                //判断行                if (k == x[i][a])                    return true;                //判断列                if (k == x[a][j])                    return true;                //判断小九宫格                if (k == x[3 * m + a / 3][3 * n + a % 3])                    return true;            }            return false;        }        public static void main(String[] args) {            System.out.println("test");            back(0, 0);            System.out.println("test");            for (int i = 0; i < 9; i++) {                for (int j = 0; j < 9; j++) {                    if (j < 8)                        System.out.print(bestx[i][j] + " ");                    else {                        System.out.println(bestx[i][j]);                    }                }            }        }    }

/这里写图片描述
(4) 体会
回溯算法一般都有一个套路,先找出问题的解空间,再思考回溯方法。
用回溯算法搜索子集树的一般算法可以描述为:

    void back(int i){    if (i>n) output(x);    else     for (int j=0 to n){    x[t]=i,    if (constraint(i)&&bound(i)) back(i+1)    }    }
0 0
原创粉丝点击