题目意思:
有一块草坪,长为l,宽为w ,在它的中心线上装有n个点状的喷水装置 , 效果是让以它为中心半径为ri的圆被润湿 , 选择尽量少的喷水装置把整个草坪全部润湿。
解题思路:
1:贪心,区间覆盖
2:这道题就是一个区间覆盖的变形,只是没有那么的直观而已。刚开始区间求错了,直接求出最左边和最右边的左边以此来作为区间端点,后来WA了近20次才发现考虑错了,嗨,只怪自己思维不够严谨。真正的区间求法是,把这个圆和这个矩形两边的交点的横坐标求出来就是区间的端点值,注意这里由于圆很大所以左边或右边可能超过规定的长度L,所以求区间时候我们不用考虑L,因为没有影响,最后只要按照区间覆盖的思路就可以求出最小的区间数。
3:区间完全覆盖问题
1 问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),求最少使用多少条线 段可以将整个区间完全覆盖
2 样例:
区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]
3解题过程:
1将每一个区间按照左端点递增顺序排列,拍完序后为[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8]
2设置一个变量表示已经覆盖到的区域。再剩下的线段中找出所有左端点小于等于当前已经覆盖到的区域的右端点的线段中,右端点最大的线段在加入,直到已经覆盖全部的区域
3过程:
假设第一步加入[1,4],那么下一步能够选择的有[2,6],[3,5],[3,6],[3,7],由于7最大,所以下一步选择[3,7],最后一步只能选择[6,8],这个时候刚好达到了8退出,所选区间为3
4贪心证明:
需要最少的线段进行覆盖,那么选取的线段必然要尽量长,而已经覆盖到的区域之前的地方已经无所谓了,(可以理解成所有的可以覆盖的左端点都是已经覆盖到的地方),那么真正能够使得线段更成的是右端点,左端点没有太大的意义,所以选择右端点来覆盖
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> using namespace std; const int MAXN = 10010; int n; double len , wide; struct seg{ double left; double right; bool operator<(const seg& s1)const{ if(left < s1.left) return true; else if(left == s1.left && right > s1.right) return true; return false; } }; seg s[MAXN]; int solve(){ if(s[0].left > 0) return -1; int ans = 1; double pre = 1e-8; for(int i = 0 ; s[i].left <= 0 && i < n ; i++) pre = max(pre , s[i].right); while(1){ if(pre >= len) return ans; double Max = 1e-8; for(int i = 0 ; s[i].left <= pre && i < n ; i++) Max = max(Max , s[i].right); if(Max <= pre) return -1; else{ ans++; pre = Max; } } } int main(){ int pos; double d , r; while(scanf("%d%lf%lf" , &n , &len , &wide) != EOF){ pos = 0; for(int i = 0 ; i < n ; i++){ scanf("%lf%lf" , &d , &r); double tmp = sqrt(r*r-(wide/2.0)*(wide/2.0)); s[pos].left = d-tmp; s[pos++].right = d+tmp; } sort(s , s+n); printf("%d\n" , solve()); } return 0; }