蓝桥杯中的地铁换乘问题

如题:

为解决交通难题,某城市修建了若干条交错的地铁线路,线路名及其所属站名如stations.txt所示。

线1 苹果园 .... 四惠东

线2 西直门 车公庄 .... 建国门

线4 ....

其中第一行数据为地铁线名,接下来是该线的站名。 当遇到空行时,本线路站名结束。

下一行开始又是一条新线....直到数据结束。

如果多条线拥有同一个站名,表明:这些线间可以在该站换车。

为引导旅客合理利用线路资源,解决交通瓶颈问题,该城市制定了票价策略:

1. 每条线路可以单独购票,票价不等。 2. 允许购买某些两条可换乘的线路的联票。联票价格低于分别购票。

单线票价和联合票价如 price.txt 所示。

线1 180 ..... 线13 114 线1,线2 350 线1,线10 390 .....

每行数据表示一种票价 线名与票价间用空格分开。如果是联票,线名间用逗号分开。 联票只能包含两条可换乘的线路。

现在的问题是:根据这些已知的数据,计算从A站到B站最小花费和可行的换乘方案。

比如,对于本题目给出的示例数据

如果用户输入: 五棵松,奥体中心

程序应该输出: -(线1,线10)-线8 = 565

如果用户输入: 五棵松,霍营

程序应该输出: -线1-(线4,线13) = 440

可以看出,用户输入的数据是:起始站,终到站,用逗号分开。 程序输出了购票方案,在括号中的表示联票,短横线(-)用来分开乘车次序。 等号后输出的是该方案的花费数值。

请编程解决上述问题。 注意: 1. 我们测试您的程序时,所用数据与题目中的示例数据不同,但格式完全一样。 2. 当多个方案有相同的最小花费,输出任意一个方案即可。

要求考生把所有类写在一个文件中。 调试好后,存入与考生文件夹下对应题号的“解答.txt”中即可。 相关的工程文件不要拷入。请不要使用package语句。

另外,源程序中只能出现JDK1.5中允许的语法或调用。不能使用1.6或更高版本。

核心思路:首先根据读取的文件建立路线的邻接矩阵, 然后穷举所有符合条件的路径, 最后选取最优的。

package final_match;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 解决地铁线路的乘换问题

 * @author wangjing
 *
 */
public class ChooseTrainNum {
    // 保存线路及该线路的所有站台
    private Map<String, ArrayList<String>> stations = 
            new HashMap<>();
    // 保存每条线路的票价及联票票价
    private Map<String, Integer> price = 
            new HashMap<>();
    // 读取文件的流
    private FileReader fr = null;
    // 邻接矩阵(表示无向图)
    private int[][] matrix = null;
    // 顶点的值
    private Object[] vexs = null;
    // 查找路径的标记
    private boolean[] visited = null;
    
    /**
     * 读取车子以及票价文件
     * @param f
     *             给定文件
     */
    public void readFile(File f) {
        ArrayList<String> al = null;
        String tmp = "";
        BufferedReader br = null;
        String name = f.getName();
        String[] s = null;
        boolean flag = false;
        try {
            // 读取的是车子文件
            if(name.startsWith("stations"))
                al = new ArrayList<>();
            fr = new FileReader(f);
            br = new BufferedReader(fr);
            while((tmp = br.readLine()) != null) {
                // 如果条件成立,表示该次读取结束, 准备开始读取下一条线路、
                // (针对读取车站而言的)
                if(tmp.equals("")) {
                    al = new ArrayList<>();
                    flag = false;
                    continue;
                }
                if(!flag) {
                    if(name.startsWith("stations"))
                        stations.put(tmp, al);
                    else {
                        s = tmp.split(" ");
                        price.put(s[0], Integer.parseInt(s[1]));
                    }
                    flag = true;
                } else {
                    if(name.startsWith("stations"))
                        al.add(tmp);
                    else {
                        s = tmp.split(" ");
                        price.put(s[0], Integer.parseInt(s[1]));
                    }
                }
                
            } // end while
            // 如果读取的是车站文件, 创建邻接矩阵
            if(name.startsWith("stations"))
                createMatrix();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(br != null)
                    br.close();
                if(fr != null)
                    fr.close();
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 根据路线,创建邻接矩阵(存储无向图)
     */
    private void createMatrix() {
        int len = stations.size();
        vexs = stations.keySet().toArray();
        matrix = new int[len][len];
        int i = 0, j = 0;
        ArrayList<String> row = null;
        ArrayList<String> col = null;
        for(; i<len; i++) {
            row = stations.get(vexs[i]);
            for(j=0; j<len; j++) {
                col = stations.get(vexs[j]);
                if(isIntersect(row, col)) {
                    matrix[i][j] = 1;
                    matrix[j][i] = 1;
                } // end if
            } // end for
        } // end for
    }
    
    /**
     * 判断给定的两条线路是否有交集
     * @param row
     *             给定的线路
     * @param col
     *             给定的线路
     * @return
     *             如果有交集,返回true;否则返回false
     */
    private boolean isIntersect(ArrayList<String> row, ArrayList<String> col) {
        if(row == col)
            return false;
        int len = col.size(), i = 0;
        for(; i<len; i++)
            if(row.contains(col.get(i)))
                return true;
        return false;
    }
    
    /**
     * 得到最优方案
     * @param begin
     *             始发站
     * @param end
     *             终点站
     * @return
     *             从始发站到终点站的最优方案
     */
    public String optimalScheme(String begin, String end) {
        ArrayList<String> bgn = location(begin);
        ArrayList<String> ed  = location(end);
        int len1 = bgn.size(), len2 = ed.size();
        int i = 0, j = 0;
        int m = 0, n = 0;
        ArrayList<String[]> tmp = null;
        String[] st = null;
        ArrayList<ArrayList<String[]>> result = new ArrayList<>();
        visited = new boolean[vexs.length];
        st = new String[vexs.length + 1];
        for(; i<len1; i++) {
            m = indexOf(bgn.get(i));
            for(j=0; j<len2; j++) {
                tmp = new ArrayList<>();
                n = indexOf(ed.get(j));
                onePath(m, n, tmp, st, 0);
                result.add(tmp);
            } // end for    
        } // end for
//        用于测试
//        len1 = result.size();
//        for(i=0; i<len1; i++) {
//            tmp = result.get(i);
//            len2 = tmp.size();
//            for(j=0; j<len2; j++) {
//                st = tmp.get(j);
//                for(m=0; m<st.length; m++)
//                    System.out.print(st[m] + " ");
//                System.out.println();
//            }
//        }
        return optimal(result);
    }
    
    /**
     * 从分析所得的所有结果中找到一个最优的解
     * @param result
     *             所有的路线结果
     * @return
     *             最优的路线方案
     */
    private String optimal(ArrayList<ArrayList<String[]>> result) {
        int size = result.size(), i = 0, j = 0;
        ArrayList<String[]> tmp = null;
        Object[] keys = price.keySet().toArray();

        String s[] = null, ss[] = null;
        int len = 0;
        Map<String, Integer> map = new HashMap<>();
        for(; i<size; i++) {
            tmp = result.get(i);
            len = tmp.size();
            for(j=0; j<len; j++) {
                /****************************\
                 *  分别求的正向和负向的最小路径选取其中
                 *  最小花费的那一组, 进过处理后即为该次
                 *  的最优.(应为可以购买联票的线路无序无序)
                \****************************/
                s = oneMin(tmp.get(j), keys);
                ss = oneMin(reverse(tmp.get(j)), keys);
                // 正向的结果,直接添加
                if(Integer.parseInt(s[1]) <= Integer.parseInt(ss[1]))
                    map.put(s[0], Integer.parseInt(s[1]));
                else {
                    // 负向的结果,要change
                    change(ss);
                    map.put(ss[0], Integer.parseInt(ss[1]));
                }
            }
        } // end for
        
        // 从所有的结果中找取一个最优的
        int min = 0, loc = 0;
        keys = map.keySet().toArray();
        min = map.get(keys[0]);
        for(i=1; i<keys.length; i++) {
            if(min > map.get(keys[i])) {
                min = map.get(keys[i]);
                loc = i;
            }
        } // end for
        return keys[loc] + " = " + min;
    }
    
    /**
     * 处理给定的一条路径
     * @param ss
     *             ss[0]是路径,ss[1]是其总票价
     */
    private void change(String[] ss) {
        // 拆分
        String[] s = ss[0].split("-");
        int i = 0, j = s.length-1;
        String tmp = "";
        // 交换
        while(i <= j) {
            tmp = s[j];
            s[j] = s[i];
            s[i] = tmp;
            i ++; j --;
        } // end while
        tmp = "";
        // 重新复合
        for(i=0; i<s.length-1; i++)
            tmp += ('-' + s[i]);
        ss[0] = tmp;
    }
    
    /**
     * 将给定的路径翻转
     * @param path
     *             给定的路径
     * @return
     *             翻转过后的路径
     */
    private String[] reverse(String[] path) {
        String[] tmp = new String[path.length];
        int i = 0, j = 0;
        while(i < path.length && path[i] != null)
            i ++;
        i --;
        for(j=0; i >=0; j++)
            tmp[j] = path[i--];
        return tmp;
    }

    /**
     * 求的一条路径的最优组合(因为有联票影响)
     * 算法:<br />
     * 每次都比较三个(不够的另外处理), 假设为a, b, c <br />
     * 1. 如果 a 与 b 有联票<br />
     *         1)     如果b 与 c有联票<br />
     *             (1) 如果money(a,b) < money(b, c)<br />
     *                     total += money(a, b);<br />
     *             (2) 否则:total += money(a);<br />
     *         2)  否则:<br />
     *             (1) total += money(a, b);<br />
     *                 i ++;<br />
     * 2:否则:<br />
     *   1) 如果 path[a] != null, total += money(a)<br />
     *   2) 否则,break;<br />
     * @param path
     *             给定的路径站点数组
     * @param keys
     *             所有的票价对应的线路数组
     * @return
     *             该条线路的最优的组合([0]表示最优路径组合, [1]表示总的票价)
     */
    private String[] oneMin(String[] path, Object[] keys) {
        int m1 = 0, m2 = 0, i = 0, loc = -1, money = 0;
        String way = "";
        for(i=1; i<path.length; i++) {
            if((loc = findUnionTicket(path[i-1] + "," + path[i], keys)) != -1) {
                m1 = price.get(keys[loc]);
                if(i < path.length-1) {
                    loc = findUnionTicket(path[i] + "," + path[i+1], keys);
                    if(loc != -1) {
                        m2 = price.get(keys[loc]);
                        if(m1 < m2) {
                            way += ("-(" + path[i-1] + "," + path[i] + ")");
                            money += m1;
                        } else {
                            way += ("-" + path[i-1]);
                            money += price.get(path[i-1]);
                        } // end if
                    } else {
                        money += m1;
                        way += ("-(" + path[i-1] + "," + path[i] + ")");
                        i ++;
                    } // end if
                } else {
                    money += m1;
                    way += ("-(" + path[i-1] + "," + path[i] + ")");
                } // end if
            } else {
                if(path[i-1] != null) {
                    way += ("-" + path[i-1]);
                    money += price.get(path[i-1]);
                } else
                    break;
            } // end if
        }
        return new String[]{way, money + ""};
    }
    
    /**
     * 查找联票位置索引
     * @param path
     *             站点的组合
     * @param keys
     *             所有的票价对应的线路数组
     * @return
     *             如果存在,返回位置, 否则返回 -1
     */
    private int findUnionTicket(String path, Object[] keys) {
        if(path == null)
            return -1;
        int i = 0;
        for(; i<keys.length; i++)
            if(path.equals(keys[i]))
                return i;
        return -1;
    }

    /**
     * 得到从begin到end中间所有合法路径组合
     * (有点类似于图的深度优先算法(DFS))
     * @param begin
     *             始发站的索引
     * @param end
     *             终点站的索引
     * @param tmp
     *             保存所有合法路径上的动态数组
     * @param or
     *             临时储存路径的数组
     * @param level
     *             递归的深度(要修改的or的下标)
     */
    private void onePath(int begin, int end, ArrayList<String[]> tmp, 
            String[] or, int level) {
        visited[begin] = true;
        or[level] = (String) vexs[begin];
        int w = 0;
        for(w=firstAdjVex(begin); w>=0 && w!=end; w=nextAdjVex(begin, w)) {
            if(!visited[w])
                onePath(w, end, tmp, or, level+1);
        }
        if(w == end) {
            or[level + 1] = (String) vexs[end];
            tmp.add(or.clone());
            or[level+1] = null;
        }
        or[level] = null;
        visited[begin] = false;
    }
    /**
     * 求的第 v行离w最近的符合要求的顶点索引
     * @param v
     *             邻接矩阵的第v行        
     * @param w
     *             第v行的w索引
     * @return
     *             w的第一邻接点索引
     */
    private int nextAdjVex(int v, int w) {
        for(int j=w+1; j<vexs.length; j++)
            if(matrix[v][j] == 1)
                return j;
        return -1;
    }
    /**
     * 求的v的第一邻接点
     * @param v
     *             第v行
     * @return
     *             v的第一邻接点索引
     */
    private int firstAdjVex(int v) {
        int j = 0;
        for(; j<vexs.length; j++)
            if(matrix[v][j] == 1)
                return j;
        return -1;
    }
    /**
     * 得到给定的顶点位置索引
     * @param content
     *                 给定的顶点
     * @return
     *                 返回位置
     */
    private int indexOf(String content) {
        int i = 0;
        for(; i<vexs.length; i++)
            if(vexs[i].equals(content))
                return i;
        return -1;
    }
    /**
     * 定位车站所在的线路
     * @param station
     *                 给定车站
     * @return
     *                 进过该车站的所有线路
     */
    private ArrayList<String> location(String station) {
        ArrayList<String> rtn = new ArrayList<>();
        Iterator<String> it = stations.keySet().iterator();
        String tmp = "";
        while(it.hasNext()) {
            tmp = it.next();
            if(stations.get(tmp).contains(station))
                rtn.add(tmp);
        } // end while
        return rtn;
    }
    /**
     * 打印邻接矩阵
     */
    public void printMatrix() {
        int i = 0, j =0;
        for(; i<matrix.length; i++)
            System.out.print(vexs[i] + " ");
        System.out.println();
        for(i=0; i<matrix.length; i++) {
            for(j=0; j<matrix.length; j++)
                System.out.print(matrix[i][j] + "\t");
            System.out.println();
        }
    }
    public static void main(String[] args) throws IOException {
        ChooseTrainNum bk = new ChooseTrainNum();
        File f = new File("price.txt");
        bk.readFile(f);
        f = new File("stations.txt");
        bk.readFile(f);
//        bk.printMatrix();
        BufferedReader br = 
                new BufferedReader(new InputStreamReader(System.in));
        String line = br.readLine();
        String rs = "";
        String[] tmp = null;
        tmp = line.split(",");
        rs = bk.optimalScheme(tmp[0], tmp[1]);
        System.out.println(rs);
    }

}

测试截图:
(文件一测试结果)

(文件二测试结果)

(文件三测试结果)

转载于:https://www.cnblogs.com/wangjing-wust/archive/2012/05/19/2508897.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值