Travelling by Stagecoach(POJ2686)

题目:

有一个旅行家计划乘马车旅行,他所在的国家共有m个城市,在城市之间有若干条道路相连。 从某个城市沿着这条道路到相邻的城市需要乘坐马车。而乘坐马车需要使用车票,每使用一张车票, 只可以通过一条道路。每张车票上都记有马的匹数,从一个城市移动到另一个城市的所需时间 等于城市之间的长度除以马的数量的结果。这位旅行家一共有n张车票,第i张车票的马的匹数是ti。 一张车票只能使用一次,并且换乘所需要的时间可以忽略。求从城市a到城市b所需要的最短时间。 如果无法到达请输出"Impossible"

 

n=2,m=4,a=2,b=1,t={3,1}.

虽然可以把城市看做顶点,道路看做边建图,但是由于有车票相关的限制,无法直接使用Dijkstra算法。不过这种情况只需要把状态作为顶点,而把状态的转移看成边来建图就可以很好地避免这个问题。

考虑“现在在城市v,此时还剩下的车票集合为S”这个状态。从这个状态出发,使用一张车票 i ∈ S,移动到相邻的城市u,就相当于状态转移到了“在城市u,此时还剩下的车票集合为S \ { i }  ”这个状态。把这个转移看成一条边,那么边上的花费是 (v-u道路的长度)/ ti, 集合使用状态压缩方法。

~(1<<i)将第 i 位设位0,s&~(1<<n) 将S里将i为设为0.表示选取第i张票。

代码如下:

package _状态压缩DP;

import java.util.Arrays;
/*
有一个旅行家计划乘马车旅行,他所在的国家共有m个城市,在城市之间有若干条道路相连。
从某个城市沿着这条道路到相邻的城市需要乘坐马车。而乘坐马车需要使用车票,每使用一张车票,
只可以通过一条道路。每张车票上都记有马的匹数,从一个城市移动到另一个城市的所需时间
等于城市之间的长度除以马的数量的结果。这位旅行家一共有n张车票,第i张车票的马的匹数是ti。
一张车票只能使用一次,并且换乘所需要的时间可以忽略。求从城市a到城市b所需要的最短时间。
如果无法到达请输出"Impossible"
* */
public class _POJ2686 {
    static int n, m, a, b;
    static int INF = 100000;
    static int[] t = new int[100];
    static int[][] d = new int[100][100];//图的邻接矩阵表示,-1表示没有边
    static double[][] dp = new double[100][100];//dp[s][v] 到达剩下的车票集合为S,并且现在在城市v的状态所需要的最小花费。

    static void solve() {

        for (int i = 0; i < 1 << n; i++) {//1<<n表示n个状态选或不选,即有n张车票
            Arrays.fill(dp[i], INF);
        }
        dp[(1 << n) - 1][a - 1] = 0;//车票一张没用,现在在a地的最小花费为0
        double res = INF;
        for (int s = (1 << n) - 1; s >= 0; s--) {// 车票的集合
            res = Math.min(res, dp[s][b - 1]);  //res 是剩下S张票到b点的最小值
            for (int v = 0; v < m; v++) {      //总共m个城市
                for (int i = 0; i < n; i++) {   //总共n张票
                    if ((s >> i & 1) == 1) {     //第i张票还没被使用的话
                        for (int u = 0; u < m; u++) {
                            if(d[v][u]>=0){
                                //使用车票i,从v移动到u
                                //将第i位设为0,其余位为1,进行&操作,就是使用了第i张票,第i位&成0
                                //到u到最小值是可用的票里 dp[s][v]+(double)d[v][u]/t[i] 最小的一个
                                dp[s&~(1<<i)][u]=Math.min(dp[s&~(1<<i)][u],dp[s][v]+(double)d[v][u]/t[i]);
                            }
                        }
                    }
                }
            }
        }
        if(res==INF){
            //无法到达
            System.out.println("Impossible");
        }else{
            System.out.printf("%.3f\n",res);
        }
    }

    public static void main(String[] args) {
        n=2;m=4;a=2;b=1;
        t[0]=3;
        t[1]=1;
        for (int i = 0; i <100 ; i++) {
            for (int j = 0; j <100 ; j++) {
                d[i][j]=-1;
            }
        }
        d[0][2]=3;
        d[2][0]=3;
        d[0][3]=2;
        d[3][0]=2;
        d[1][2]=3;
        d[2][1]=3;
        d[1][3]=5;
        d[3][1]=5;
        solve();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值