ThoughtWorks 一道面試題及解法


前兩天面試ThoughtWorks,有一道家庭作業題,題目如下:

Problem Two: Conference Track Management
 
You are planning a big programming conference and have received many proposals which have passed the initial screen process but you're having trouble 
fitting them into the time constraints of the day -- there are so many possibilities! So you write a program to do it for you. The conference has multiple tracks each of which has a morning and afternoon session. Each session contains multiple talks. Morning sessions begin at 9am and must finish before 12 noon, for lunch. Afternoon sessions begin at 1pm and must finish in time for the networking event. The networking event can start no earlier than 4:00 and no later than 5:00. No talk title has numbers in it. All talk lengths are either in minutes (not hours) or lightning (5 minutes). Presenters will be very punctual; there needs to be no gap between sessions. Note that depending on how you choose to complete this problem, your solution may give a different ordering or combination of talks into tracks.
This is acceptable; you don’t need to exactly duplicate the sample output given here. Test input: Writing Fast Tests Against Enterprise Rails 60min Overdoing it in Python 45min Lua
for the Masses 30min Ruby Errors from Mismatched Gem Versions 45min Common Ruby Errors 45min Rails for Python Developers lightning Communicating Over Distance 60min Accounting-Driven Development 45min Woah 30min Sit Down and Write 30min Pair Programming vs Noise 45min Rails Magic 60min Ruby on Rails: Why We Should Move On 60min Clojure Ate Scala (on my project) 45min Programming in the Boondocks of Seattle 30min Ruby vs. Clojure for Back-End Development 30min Ruby on Rails Legacy App Maintenance 60min A World Without HackerNews 30min User Interface CSS in Rails Apps 30min Test output: Track 1: 09:00AM Writing Fast Tests Against Enterprise Rails 60min 10:00AM Overdoing it in Python 45min 10:45AM Lua for the Masses 30min 11:15AM Ruby Errors from Mismatched Gem Versions 45min 12:00PM Lunch 01:00PM Ruby on Rails: Why We Should Move On 60min 02:00PM Common Ruby Errors 45min 02:45PM Pair Programming vs Noise 45min 03:30PM Programming in the Boondocks of Seattle 30min 04:00PM Ruby vs. Clojure for Back-End Development 30min 04:30PM User Interface CSS in Rails Apps 30min 05:00PM Networking Event Track 2: 09:00AM Communicating Over Distance 60min 10:00AM Rails Magic 60min 11:00AM Woah 30min 11:30AM Sit Down and Write 30min 12:00PM Lunch 01:00PM Accounting-Driven Development 45min 01:45PM Clojure Ate Scala (on my project) 45min 02:30PM A World Without HackerNews 30min 03:00PM Ruby on Rails Legacy App Maintenance 60min 04:00PM Rails for Python Developers lightning 05:00PM Networking Event

 

思路如下:

把所有的會議安排在兩天內,每天分為上午和下午,上午最多三小時(180min),9點到12點,下午最多四小時(240min),1點到5點。

其實就是給定大小的4個坑,每個會議都是一個固定大小的蘿卜,要把這所有的19個蘿卜裝到4個坑中,可以裝不滿,但蘿卜不能有剩余。

 

解法如下:

創建如下工程:

 

其中 talks-list.txt 為輸入內容,如下圖:

 

通過 FileTool 工具類將其讀取到 map 中,key 為會議名稱,value 為所用時間,FileTool 內容如下:

package harrison.ConferenceTrackManagement;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Harrison
 *
 */
public class FileTool {

    /**
     * Read talks-list.txt to map
     * 
     * @return map, key:title; value:time;
     */
    public static Map<String, Integer> readTalksList2Map() {
        Map<String, Integer> titleAndTime = new HashMap<String, Integer>();
        File file = new File("src/main/resources/talks-list.txt");
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            String line = null;
            int numTime = 0;
            while ((line = reader.readLine()) != null) {
                int lastBlank = line.lastIndexOf(" ");
                String title = line.substring(0, lastBlank);
                String time = line.substring(lastBlank + 1);
                if (time.equals("lightning")) {
                    numTime = 5;
                } else {
                    // Remove "min" suffix
                    numTime = Integer.parseInt(time.substring(0, time.length() - 3));
                }
                titleAndTime.put(title, numTime);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
        return titleAndTime;
    }
}

 

ConferenceSchedule 類負責生成具體會議的安排,實現如下:

package harrison.ConferenceTrackManagement;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;

/**
 * Conference track management
 * 
 * @author Harrison
 *
 */
public class ConferenceSchedule {

    /**
     * Get a schedule instance
     */
    public static void getSchedule() {
        Map<String, Integer> titleAndTime = FileTool.readTalksList2Map();
        Map<String, Integer> titleAndTimeBak = new HashMap<String, Integer>(titleAndTime);
        Map<String, Integer> track1Forenoon = getTrackMap(titleAndTime, 180);
        Map<String, Integer> track1Afternoon = getTrackMap(titleAndTime, 240);
        Map<String, Integer> track2Forenoon = getTrackMap(titleAndTime, 180);
        Map<String, Integer> track2Afternoon = getTrackMap(titleAndTime, 240);

        if (!titleAndTime.isEmpty()) {
            Map<String, Integer> tempMap = new HashMap<String, Integer>(titleAndTimeBak);
            while(!tempMap.isEmpty()){
                tempMap = new HashMap<String, Integer>(titleAndTimeBak);
                track1Forenoon = getTrackMap(tempMap, 180);
                track1Afternoon = getTrackMap(tempMap, 240);
                track2Forenoon = getTrackMap(tempMap, 180);
                track2Afternoon = getTrackMap(tempMap, 240);
            }
        }

        System.out.println("Track 1:");
        printForenoonSchedule(track1Forenoon);
        printAfternoonSchedule(track1Afternoon);
        System.out.println("Track 2:");
        printForenoonSchedule(track2Forenoon);
        printAfternoonSchedule(track2Afternoon);

    }

    /**
     * Print forenoon schedule
     * 
     * @param trackMap
     */
    public static void printForenoonSchedule(Map<String, Integer> trackMap) {
        int sumTime = 0;
        int res = 0;
        String remainderStr = "00";
        for (Entry<String, Integer> entry : trackMap.entrySet()) {
            String title = entry.getKey();
            int time = entry.getValue();
            String timeStr = time == 5 ? "lightning" : time + "";
            switch (res) {
            case 0:
                System.out.println("09:" + remainderStr + "AM " + title + " " + timeStr + "min");
                break;
            case 1:
                System.out.println("10:" + remainderStr + "AM " + title + " " + timeStr + "min");
                break;
            case 2:
                System.out.println("11:" + remainderStr + "AM " + title + " " + timeStr + "min");
                break;
            default:
                break;
            }
            sumTime += time;
            res = sumTime / 60;
            int remainder = sumTime % 60;
            if (remainder / 10 == 0) {
                remainderStr = "0" + remainder;
            } else {
                remainderStr = remainder + "";
            }
        }
        System.out.println("12:00PM Lunch");
    }

    /**
     * Print afternoon schedule
     * 
     * @param trackMap
     */
    public static void printAfternoonSchedule(Map<String, Integer> trackMap) {
        int sumTime = 0;
        int res = 0;
        String remainderStr = "00";
        for (Entry<String, Integer> entry : trackMap.entrySet()) {
            String title = entry.getKey();
            int time = entry.getValue();
            String timeStr = time == 5 ? "lightning" : time + "";
            switch (res) {
            case 0:
                System.out.println("01:" + remainderStr + "PM " + title + " " + timeStr + "min");
                break;
            case 1:
                System.out.println("02:" + remainderStr + "PM " + title + " " + timeStr + "min");
                break;
            case 2:
                System.out.println("03:" + remainderStr + "PM " + title + " " + timeStr + "min");
                break;
            case 3:
                System.out.println("04:" + remainderStr + "PM " + title + " " + timeStr + "min");
                break;
            default:
                break;
            }
            sumTime += time;
            res = sumTime / 60;
            int remainder = sumTime % 60;
            if (remainder / 10 == 0) {
                remainderStr = "0" + remainder;
            } else {
                remainderStr = remainder + "";
            }
        }
        System.out.println("05:00PM Networking Event");
    }

    /**
     * Get each session track
     * 
     * @param titleAndTime
     * @param totalMinute
     * @return
     */
    public static Map<String, Integer> getTrackMap(Map<String, Integer> titleAndTime, int sessionMinute) {
        Map<String, Integer> trackMap = new HashMap<String, Integer>();
        List<String> titleList = new ArrayList<String>(titleAndTime.keySet());
        Random random = new Random();
        int randomIndex = 0;
        String randomTitle = null;
        int time = 0;
        int sumTime = 0;
        while (sumTime < sessionMinute && titleList.size() > 0) {
            randomIndex = random.nextInt(titleList.size());
            randomTitle = titleList.get(randomIndex);
            time = titleAndTime.get(randomTitle);
            sumTime += time;
            if (sumTime <= sessionMinute) {
                trackMap.put(randomTitle, time);
            }
            titleList.remove(randomTitle);
        }

        // Remove entry from titleAndTime which has already schedule
        Set<String> trackMapKeySet = trackMap.keySet();
        Iterator<Entry<String, Integer>> it = titleAndTime.entrySet().iterator();
        while (it.hasNext()) {
            if (trackMapKeySet.contains(it.next().getKey())) {
                it.remove();
            }
        }
        return trackMap;
    }
}

其中,getTrackMap 方法用於隨機從所有蘿卜中挑選出一組,其和不能大於所指定的大小,即坑的大小,由參數 sessionMinute 指定。getSchedule 方法通過調用 getTrackMap 依次生成 4 個坑中的數據,因為 getTrackMap 是隨機挑選蘿卜,可能出現所有蘿卜並未全部入坑的情況,所以當分配失敗時,在 getSchedule 中做了重試。如果分配成功,則調用 printForenoonSchedule 方法和 printAfternoonSchedule 方法格式化輸出。

一個可能的解如下:

再次運行,產生另一個可能的解決,每次運行的結果並不一定相同,如下:

至此,實現完畢,但是這種做法並未得到面試官的認可,如有更優解,歡迎指教!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM