《经典算法》货郎担

题目:

如果对任意数目的n个城市,分别用1~n的数字编号表示。则这个问题归结为在有向赋权图G=<V,E>中,寻找一条路径最短的哈密尔顿回路问题。其中,V={1,2,...,n}表示城市顶点;边(i,j)\inE表示城市i到城市j的距离,i,j=1,2,...,n。这样可用邻接矩阵C来表示各个城市之间的距离,计费用矩阵。如果(i,j)\inE,则c_ij>0;否则,c_ij=\infty

思路:

令d(i,\bar{V})表示从顶点i出发,经\bar{V}中各个顶点一次,最终回到初始出发点的最短路径的长度。开始时\bar{V} = V-{i}。于是,可以定义下面的动态规划函数:

d(i,V-{i}) = d(i,\bar{V}) = min {c_ik + d(k,\bar{V}-{k})}(k\in\bar{V}​​​​​​​)

d(k,\varphi) = C_ki  k\neqi

例子:

C = (c_ij)=\begin{pmatrix} \infty &3 &6 &7 \\ 5&\infty &2 &3 \\ 6&4 &\infty &2 \\ 3&7 &5 & \infty \end{pmatrix}

d(1,{2,3,4}) = min{c_12+d(2,{3,4}),c_13+d(3,{2,4}),c_14+d(4,{2,3})}

d(2,{3,4}) = min{c_23+d(3,{4}),c_24+d(4,{3})};

d(3,{2,4}) = min{c_32+d(2,{4}),c_34+d(4,{2})};

d(4,{2,3}) = min{c_42+d(2,{3}),c_43+d(3,{2})};

依次类推;

源码:

package com.wyj.cargoCarrier;

import java.util.Scanner;

public class cargoCarrier {
    private static int n;            //城市个数
    private static int[][] cost;    //费用矩阵
    private static int start;        //起始位置
    private static int[] path;        //最优路径
    private static int sum = 0;        //最优路径权值和
    private static boolean[]flag;        //0未访问,1已被访问

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入城市个数 n=");
        n = sc.nextInt();
        System.out.println("请输入城市之间的权值代价(注:城市自己到自己记为0意为无穷的意思)");
        cost = new int[n][n];
        flag = new boolean[n];
        for(int i=0;i<n;i++) {
            for(int j=0;j<n;j++) {
                System.out.print("城市"+(i+1)+"到城市"+(j+1)+"直接权值是:");
                cost[i][j] = sc.nextInt();
            }
        }
        printFun();
        while(true) {
            System.out.print("输入起始位置start=");
            start = sc.nextInt();
            if((start>=1&&start<=n)) {
                break;
            }
        }
        initFun();
        System.out.println("最小费用"+workFun(n,start-1));
    }
    /**
     * 这里先来了解一下算法思路:
     *         1:由上面树状图我们不难解除此算法需要两层循环
     *         2:每次循环遍历的节点总是剔除了所有的父辈节点,比如当你是d(2,{3,4})的时候就不用访问节点1,当你是d(3,{4})的时候就不用访问节点1,2
     *         3:由2不难看出需要一个以为数组判断当前节点是否已被访问记flag[n]
     */
    public static int workFun(int num,int spot) {
        if(num==1) {//极限情况,当只剩最后一个节点则返回该节点到起点的距离,这里是一个回路所以返回的是cost[spot][start],如果只是遍历所有点不用返回起始则直接返回0
            return cost[spot][start-1];
        }
        int mincost = Integer.MAX_VALUE;
        for(int i=0;i<n;i++) {
            if(!flag[i]&& i!=(start-1)) {//该节点在此次循环中未被访问,并且该节点不是起始节点
                if(mincost <= cost[spot][i]+cost[i][start]){
                    continue; //其作用为结束本次循环。即跳出循环体中下面尚未执行的语句。区别于break 
                }
                flag[i] = true;//递归调用时,防止重复调用
                int value = cost[spot][i]+workFun(num-1, i);
                if(value<mincost) {
                    mincost = value;
                }
                flag[i] = false;//本次递归调用完毕,让下次递归调用
            }
        }
        return mincost;
        
    }
    //答应费用矩阵
    public static void printFun() {
        for(int i=0;i<n;i++) {
            for(int j=0;j<n;j++) {
                System.out.print(cost[i][j]+" ");
            }
            System.out.println();
        }
    }
    //初始化flag,所有节点未访问
    private static void initFun() {
        for(int i=0;i<flag.length;i++) {
            flag[i] = false;
        }
    }
}
 

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值