挑战程序竞赛系列(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[N];
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[n][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[n][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[n];
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[n];
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);
}
}
}