题目来源:http://acm.pku.edu.cn/JudgeOnline/problem?id=1727
这个题目用到了排序和二分查找,贪心。
题意是现有许多点,然后再用小于给定数目的点按照规则覆盖原先所有的点,使创建的点中的最小的t最大。
先观察覆盖公式:t2>=t1+|x2-x1|,现在知道t2,x2求尽量大的t1。则我们可以枚举t1的值,只要x1存在,则表示这个新创建的点可以覆盖到(t2,x2)。所以我的想法是,从大到小枚举每一个可能的t1,对于每个t1求出覆盖至少需要创建多少个点。如果满足条件则得出答案,否则t1减少1。
对于每个t1求至少多少个点的问题,可以用贪心思路。对所有现有点按x排序。从左至右,对于每个(t2,x2),得出x1的范围。t1-t2+x2<=x1<=t2-t1+x2。逐步缩小x1的范围,当x1不可能存在的时候,表示需要创建一个新点。这样就可以确认某个t1是否合适的问题。
现在的问题就是枚举t1了,此时可以二分查找来做。先确定上限(肯定是所有现有点中t的最小值),下限为一个很大的负数,(我取的是-4000001,注意题目中的t的范围只是现有点的范围,这里wr了很几次。)然后就很自然的得出一个结果。
代码:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
1 #include <iostream>
2
3 using namespace std;
4
5 struct event
6 {
7 int t;
8 int x;
9 };
10
11 event all[100001];
12 int n,m;
13 int maxt;
14 int mint;
15
16 int cmp(const void *a,const void *b)
17 {
18 return (*(event*)a).x-(*(event*)b).x;
19 }
20
21 int greed(int maxt)
22 {
23 int mi,mx;
24 int nmi,nmx;
25 int bo=0;
26 int res=0;
27 for(int i=0;i<n;i++)
28 {
29 if(!bo)
30 {
31 mi=maxt-all[i].t+all[i].x;
32 mx=all[i].t-maxt+all[i].x;
33 res++;bo=1;
34 if(res>m) return 0;
35 }
36 else
37 {
38 nmi=maxt-all[i].t+all[i].x;
39 nmx=all[i].t-maxt+all[i].x;
40 if(nmi>mi) mi=nmi;
41 if(nmx<mx) mx=nmx;
42 if(mx<mi)
43 {
44 bo=0;i--;
45 }
46 }
47 }
48 if(res>m) return 0;
49 return 1;
50 }
51
52 int main()
53 {
54 freopen("test.txt","r",stdin);
55 int t,tt=1;
56 cin>>t;
57 while(t--)
58 {
59 maxt=1000001;
60 mint=-4000001;
61 scanf("%d%d",&n,&m);
62 for(int i=0;i<n;i++)
63 {
64 scanf("%d%d",&all[i].t,&all[i].x);
65 if(maxt>all[i].t) maxt=all[i].t;
66 }
67 qsort(all,n,sizeof(event),cmp);
68
69 //while(1)
70 //{
71 // if(maxt==mint)
72 // {
73 // printf("Case %d: %d\n",tt++,maxt);break;
74 // }
75 // int temp=(maxt+mint+1)/2;
76 // if(greed(temp))
77 // mint=temp;
78 // else maxt=temp-1;
79 //}
80 while(maxt>=mint)
81 {
82 int temp=(maxt+mint)/2;
83 if(greed(temp)) mint=temp+1;
84 else maxt=temp-1;
85 }
86 if(greed(maxt)) mint=maxt;
87 printf("Case %d: %d\n",tt++,maxt);
88 }
89 }