挑战程序竞赛系列(7):2.1一往直前!贪心法(区间)
2017-05-24 12:41
330 查看
挑战程序竞赛系列(7):2.1一往直前!贪心法
详细代码可以fork下Github上leetcode项目,不定期更新。练习题如下:
1. POJ 2376: Cleaning Shifts
2. POJ 1328: Radar Installation
3. POJ 3190: Stall Reservations
POJ 2376: Cleaning Shifts
思路:贪心,先按开始时间排序,如刚开始begin = 1时,从大于begin的所有牛中选择end最大的,为下一轮的begin时间,这样一直继续下去,直到大于T即可。代码如下:
public class SolutionDay23_P2376 { private static class Pair{ int s; int e; Pair(int s, int e){ this.s = s; this.e = e; } @Override public String toString() { return "["+s+","+e+"]"; }; } public static void main(String[] args) { Scanner in = new Scanner(System.in); int N = in.nextInt(); int T = in.nextInt(); Pair[] pairs = new Pair ; for (int i = 0; i < N; i++){ int s = in.nextInt(); int e = in.nextInt(); pairs[i] = new Pair(s,e); } System.out.println(solve(pairs,T)); in.close(); } private static int solve(Pair[] pairs, int T){ Arrays.sort(pairs, new Comparator<Pair>() { @Override public int compare(Pair o1, Pair o2) { return o1.s != o2.s ? o1.s - o2.s : o1.e - o2.e ; } }); int pos = 0; int end = 0; int step = 0; int begin = 0; while (end < T){ begin = end + 1; for (int i = pos; i < pairs.length; i++){ if (pairs[i].s <= begin){ end = Math.max(end, pairs[i].e); }else{ pos = i; break; } } if (begin > end){ return -1; } else{ step ++; } } return step; } }
POJ 1328: Radar Installation
思路:刚开始的想法是根据岛屿的y坐标进行排序,然后每次选择最大的y值,以它的x为雷达中心,把所有覆盖的岛屿删除,再选择第二个雷达,依此类推,直到没有岛屿,WA了。其实想想也挺蠢的,你就那么确定雷达的中心一定是在某个岛屿的下方?确定雷达的横坐标显然不是一个有效的做法。(因为它可以是个范围)
所以,雷达既然是范围,我们就不可能从雷达的角度去思考该问题,反过来,我们可以根据岛屿求得雷达可在的区域列表,这样计算过后,只要尽可能的让这些区域列表重合即可。
根据岛屿所在的位置,计算与x轴相交的两个点,记为start和end,计算公式如下:
以岛屿为圆心,构造: (x-x1)^2 + (y-y1)^2 = d^2; 令 y = 0; 所以有: start = x - (d*d - y*y) end = x + (d*d - y*y)
好了,知道了每个雷达可在的区域,我们就开始选择吧。目标:尽可能的让所有区域列表重合,所以我们从最左端的start开始,它会有一个end区间。现在遍历start小于end区间的所有区域,最小end区间,一旦某个start超过end,就增加一个雷达。代码如下:
public class SolutionDay24_P1328 { public static void main(String[] args) { Scanner in = new Scanner(System.in); int id = 0; while (true){ String[] info = in.nextLine().trim().split(" "); int n = Integer.parseInt(info[0]); int d = Integer.parseInt(info[1]); if (n == 0 && d == 0) break; int[][] islands = new int [2]; for (int i = 0; i < n; i++){ String[] pos = in.nextLine().trim().split(" "); islands[i][0] = Integer.parseInt(pos[0]); islands[i][1] = Integer.parseInt(pos[1]); } in.nextLine(); System.out.println("Case "+(++id)+": "+solve(islands, d)); } in.close(); } private static class Section{ double start; double end; Section(double start, double end){ this.start = start; this.end = end; } } private static int solve(int[][] islands, int d){ Section[] sections = new Section[islands.length]; for (int i = 0; i < islands.length; i++){ int x = islands[i][0]; int y = islands[i][1]; if (y > d) return -1; double sqrt = Math.sqrt(d*d-y*y); sections[i] = new Section(x-sqrt, x+sqrt); } Arrays.sort(sections, new Comparator<Section>() { @Override public int compare(Section o1, Section o2) { //强转会出错,注意这个地方 return (o1.start != o2.start ? o1.start - o2.start : o1.end - o2.end) > 0 ? 1 : -1; } }); double minEnd = -1 << 30; int step = 0; for (int i = 0; i < sections.length; i++){ if(sections[i].start <= minEnd){ minEnd = Math.min(minEnd, sections[i].end); }else{ step ++; minEnd = sections[i].end; } } return step; } }
POJ 3190: Stall Reservations
不得不吐槽下,POJ系统对JAVA的支持太弱了,优先队列的接口居然能编译错误,更别说支持Java 8的一些特性了。还是一道区间如何选择的问题,贪心策略:
总是先安排A小的那头奶牛,所以先对A进行排序,接着就是尽可能的把每头奶牛放入stall中,最糟糕的情况就是所有奶牛的喝奶区间都重合,这必然需要安排大小为奶牛数的stall来满足它们。
所以,如果区间不重,那么我们就可以把奶牛放在一个stall中,但这应该怎么操作呢?
我的想法:
针对第一头奶牛,能够得到一个end区间,此时,遍历余下的n-1头奶牛,把start > end的奶牛给筛选出来,然后更新end区间,并且删除该奶牛,直至末尾。
若还有剩余的奶牛,则再建个stall,重复上述步骤,直到所有奶牛被安置完成。
这是最基本的求解思路,但缺点就是需要遍历O(k∗n)次,k为stall数,n为奶牛数。
在上述想法中,我们还少使用了一个性质,每次安排一头新牛进入stall时,一定选择stall中end最小的那个,因为end较大的更不可能给这头新牛,必然区间重合。所以每次,只需要判断peek的stall是否满足
cow.start > stall.end,如果满足则加入stall,不满足则创建新的stall来放这头牛。(贪心在这)
代码如下:
public class SolutionDay23_P3190 { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); int[][] cows = new int [2]; for (int i = 0; i < n; i++){ cows[i][0] = in.nextInt(); cows[i][1] = in.nextInt(); } solve(cows, n); in.close(); } private static class MilkTime{ int start; int end; int id; public MilkTime(int start, int end,int id) { this.start = start; this.end = end; this.id = id; } } private static class Stall{ int end; int id; public Stall(int end, int id){ this.end = end; this.id = id; } @Override public String toString() { return "[end: "+end+" id: "+id+"]"; } } private static void solve(int[][] cows, int n){ MilkTime[] milks = new MilkTime ; for (int i = 0; i < n; i++){ milks[i] = new MilkTime(cows[i][0], cows[i][1], i); } Arrays.sort(milks,new Comparator<MilkTime>() { @Override public int compare(MilkTime o1, MilkTime o2) { return o1.start - o2.start; } }); PriorityQueue<Stall> queue = new PriorityQueue<>(new Comparator<Stall>() { @Override public int compare(Stall o1, Stall o2) { return o1.end - o2.end; } }); int[] result = new int ; for (int i = 0; i < n; i++){ if (i == 0){ put(queue, milks[i], true, result); continue; } if (milks[i].start <= queue.peek().end){ put(queue, milks[i], true, result); }else{ put(queue, milks[i], false, result); } } System.out.println(queue.size()); for (int i = 0; i < n; i++){ System.out.println(result[i]); } } private static void put(PriorityQueue<Stall> queue, MilkTime cow, boolean isNew, int[] result){ if(isNew){ int id = queue.size() + 1; result[cow.id] = id; Stall stall = new Stall(cow.end, id); queue.offer(stall); }else{ Stall stall = queue.poll(); stall.end = cow.end; result[cow.id] = stall.id; queue.offer(stall); } } }
相关文章推荐
- 挑战程序竞赛系列(8):2.1一往直前!贪心法(其他)
- 挑战程序竞赛系列(34):3.2坐标离散化
- 挑战程序竞赛系列(2):2.3优化递推关系式
- 挑战程序竞赛系列(22):3.2弹性碰撞
- 挑战程序竞赛系列(25):3.5最大权闭合图
- 挑战程序竞赛系列(24):3.5最大流与最小割
- 挑战程序竞赛系列(35):3.3Binary Indexed Tree
- 挑战程序竞赛系列(32):4.5 A*与IDA*
- 挑战程序竞赛系列(20):3.2尺取法
- 挑战程序竞赛系列(19):3.1最小化第k大的值
- 挑战程序竞赛系列(29):3.4熟练掌握动态规划
- 挑战程序竞赛系列(3):2.3需要思考的动规
- 挑战程序竞赛系列(10):2.4并查集
- 挑战程序竞赛系列(9):2.4优先队列
- 挑战程序竞赛系列(21):3.2反转
- 挑战程序竞赛系列(16):3.1最大化最小值
- 挑战程序竞赛系列(1):2.3动态规划
- 挑战程序竞赛系列(14):2.6素数
- 挑战程序竞赛系列(38):4.1模运算的世界(1)
- 挑战程序竞赛系列(31):4.5剪枝