题意大致是,x-y坐标系中,x轴上方有n个点,
那么此时让你在x轴上选若干点,以半径为d作圆,使得上方的点都坐落在圆内,并且作的圆数量最少.
一般这种在若干东西里选取若干,使得选取数量最少,一般是贪心.
这题看着不好求解,那么数学上进行转化.
对于x轴上方的点p,显然如果纵坐标y大于d时,无法被圆心在x轴上的圆圈住,此时输出-1
那么对于点p(x,y),数学角度上,建立三角形,delta=d*d-y*y,以区间[x-delta,x+delta]上的点作为圆心的圆肯定是可以包围p的.
这样就转化成区间贪心,若干区间,选取/标记最少的点,让所有区间都能被标记.
然后发现对一系列区间,如果一个区间和其他很多不同区间有重复部分,只要任选一个有交集的区间,
重复上一个操作,然后直到取不到为止
最后发现,无论是如何取有交集的区间,最后ans的值是一样的.
感觉可以用动态规划的思想证明这个贪心策略
然后直接暴力,1e6不会超时
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=100010;
const int inf=0x3f3f3f3f;
struct node {
double l,r;
}a[maxn];
bool cmp(const node &a,const node &b){
return a.l<b.l||(a.l==b.l&&a.r>b.r);
}
int n,cnt;
double x,y,d;
bool vis[maxn];
queue <int> que;
int main()
{
while(cin>>n>>d&&(n||d)){
int ans=0;
memset(vis,0,sizeof(vis));
bool flag=0;
for(int i=0;i<n;i++){
cin>>x>>y;
if(fabs(y)>d) flag=1;
double de=sqrt(d*d-y*y);
a[i].l=x-de,a[i].r=x+de;
}
if(flag){
printf("Case %d: -1\n",++cnt);
continue;
}
sort(a,a+n,cmp);
for(int i=0;i<n;i++){
if(vis[i]) continue;
while(que.size()) que.pop();
que.push(i);
ans++;
double l=a[i].l,r=a[i].r;
for(int j=0;j<n;j++){
if(vis[j]) continue;
double ll=a[j].l,rr=a[j].r;
if(ll<=r){
l=min(l,ll),r=min(r,rr);
que.push(j);
}
}
while(que.size()){
int t=que.front();que.pop();
vis[t]=1;
}
}
printf("Case %d: %d\n",++cnt,ans);
}
return 0;
}
这题一开始wa,原因竟然是我在ans=-1情况下直接输出-1...
emmm
看了题解才发现有o(n)的做法.....
这种做法基本就是poj3069的变形了
大致思路一样,就是不从区间入手,从选取的点入手,因为一段区间必然要选取某个点,那么我们每次都选择最优的.
显然这种区间问题,要先进行排序,然后选取区间的端点值,这样在有序的情况下,端点值一定是局部最优,因此推出整体最优
先对l进行降序排序,然后选取第一个点的l作为此时该区间的最优点,(显然,此时对于后面的区间,最左端覆盖得的区域最大,因此贪心策略是正确的)
然后找到无法被囊括的区间,此时再去更换temp
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=100010;
const int inf=0x3f3f3f3f;
struct node {
double l,r;
}a[maxn];
bool cmp(const node &a,const node &b){
return a.l>b.l;
}
int n,cnt;
double x,y,d;
int main()
{
while(cin>>n>>d&&(n||d)){
int ans=1;
bool flag=0;
for(int i=0;i<n;i++){
cin>>x>>y;
if(fabs(y)>d) flag=1;
double de=sqrt(d*d-y*y);
a[i].l=x-de,a[i].r=x+de;
}
if(flag){
printf("Case %d: -1\n",++cnt);
continue;
}
sort(a,a+n,cmp);
double t=a[0].l;
for(int i=1;i<n;i++){
if(a[i].r<t){
ans++;
t=a[i].l;
}
}
printf("Case %d: %d\n",++cnt,ans);
}
return 0;
}
无论是时间复杂度还是代码可读性都有大大的提升.