动态规划题:
出租车选单:
输入
1 2 3 3 开始时间
3 4 5 6 结束时间
200 150 180 210 每单的金钱
订单时间不能重合:例如,选了13 就不能选 24 ,但可以选3 5 或者 3 6
回溯思路:订单排序,按startTime,endTime
然后一次取出订单,选择接单或者不接单,(需要考虑,接单的startTime必须大于等于上一次接单的endTime)。
共有2^n种情况。
优化:可以加入的剪枝操作,当当前的startTime小于最后一次的endTime时,直接退出循环
if(quest.startTime<endTime){
maxMoney = Math.max(maxMoney,curMoney);
return;
}
用的回溯:只通过了33%
public class Xiecheng1 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // int n = Integer.valueOf(scanner.nextLine()); // String[] startTime = scanner.nextLine().split(" "); // String[] endTime = scanner.nextLine().split(" "); // String[] moneys = scanner.nextLine().split(" "); int n = 4; String[] startTime = "1 2 3 3".split(" "); String[] endTime = "3 4 5 6".split(" "); String[] moneys = "200 150 180 210".split(" "); System.out.println(maxValue(n, startTime, endTime, moneys)); } public static int maxMoney=0; private static int maxValue(int n, String[] startTime, String[] endTime, String[] moneys) { PriorityQueue<Quest> list= new PriorityQueue<>(new Comparator<Object>() { @Override public int compare(Object o1, Object o2) { Quest q1 = (Quest) o1; Quest q2 = (Quest) o2; if(q1.startTime!=q2.startTime){ return q1.startTime - q2.startTime; }else{ return q1.endTime-q2.endTime; } } }); for (int i = 0; i < n; i++) { int a = Integer.parseInt(startTime[i]); int b = Integer.parseInt(endTime[i]); int c = Integer.parseInt(moneys[i]); list.add(new Quest(a,b,c)); } Quest[] quests = new Quest[n]; for (int i = 0; i < n; i++) { quests[i] = list.poll(); } solution(0,n,0,0,quests); return maxMoney; } public static void solution(int c, int n, int endTime,int curMoney, Quest[] quests){ if(c == n){ maxMoney = Math.max(maxMoney,curMoney); return; } // 这次选 Quest quest = quests[c]; if(quest.startTime>=endTime){ solution(c+1,n,quest.endTime,curMoney+quest.money,quests); } // 这次不选 solution(c+1,n,endTime,curMoney,quests); } } class Quest{ public Quest(int startTime,int endTime, int money){ this.startTime = startTime; this.endTime = endTime; this.money = money; } public int startTime; public int endTime; public int money; }
在复习了01背包以及完全背包问题后:对该问题进行了重新解答:
/** * 共有n个单子:尽可能多的接单,使收益最大,时间不重叠 * s:开始时间,e:结束时间,m:本单的收益 * */ @Test public void test2(){ int n = 6; int[] starts = {1,1,2,3,4,5}; int[] ends = {2,3,4,5,6,6}; int[] moneys = {50,100,150,200,180,90}; System.out.println(maxMoney(n, starts, ends, moneys)); } public int maxMoney(int n, int[] starts, int[] ends, int[] moneys){ int maxLength = Arrays.stream(ends).max().getAsInt(); // dp[i][j] 前i个订单中,不重合,选到endTime时间达到j时最大利润 int[][] dp = new int[n][maxLength+1]; // 初始化 for(int i=0; i <= maxLength; i++){ if(i==ends[0]){ dp[0][i] = moneys[0]; } } for(int i=1; i<n; i++){ for(int j=0; j<=maxLength; j++){ if(j==ends[i]) { // 当前任务选了之后达到j 以及 当前任务没选,之前选了达到的j // 如果选当前任务,那么就从starts之前的最大值与当前money结合 // temp表示:上一层中在starts[i]之前结束的最大money int temp = 0; for (int k = 0; k <= starts[i]; k++) { temp = Math.max(dp[i - 1][k], temp); } // 如果不选当前任务 dp[i][j] = Math.max(temp + moneys[i], dp[i - 1][j]); }else if(j<ends[i]) { // 本次没有选择当时到了j dp[i][j] = dp[i - 1][j]; } if(j>ends[i]){ break; } } } int ret=0; for(int i=0; i<=maxLength; i++){ ret = Math.max(ret,dp[n-1][i]); } for (int[] ints : dp) { for (int anInt : ints) { System.out.print(anInt+" "); } System.out.println(); } return dp[n-1][maxLength]; }