poj 3258 River Hopscotch 题解
2016-07-04 08:37
246 查看
【题意】
牛要到河对岸,在与河岸垂直的一条线上,河中有N块石头,给定河岸宽度L,以及每一块石头离牛所在河岸的距离,
现在去掉M块石头,要求去掉M块石头后,剩下的石头之间以及石头与河岸的最小距离的最大值。
【解法】
用二分做,但是开始写了三个版本的二分,全都wa。
无赖看了别人的二分,还是不理解,为什么他们写的就能过。
反复思索后,终于明白了:关键在于题目求的是什么。
做题思想:二分所求的最小距离的最大值mid,记录可以去掉的石头块数cnt(注意:当相邻的石头的距离小于等于mid,就可以去掉),
若cnt > M,h = h - 1 (这里是比较难理解的,也是我纠结挺久的地方),此时,应当回过头来看一下题目求的是什么:
寻找一个长度mid,使得可以去掉M块石头,剩下的石头中,石头间的最小距离为mid。
但是cnt记录的是:相邻距离小于等于mid的块数,所以存在这样的一种情况 ---- cnt记录的所有石头中,
有很多块石头间的距离是等于mid,而距离小于mid的石头的块数是小于等于M的,此时,若将h的值减一,
那么h的值就变成一个小于题目所求的答案了。
举个例子就懂了:假设有9块石头,首尾的数都表示河岸。
石头的编号 1 2 3 4 5 6 7 8 9
石头到河岸的距离 0 4 5 7 9 12 16 19 23 26 28
相邻的距离 4 1 2 2 3 4 3 3 3 2
假设l = 1,h = 5, M = 4 则mid = 3, 此时去掉的石头的编号为:2,3,5,7,9 cnt = 5
按照程序,h = h - 1 = 4,此时mid = 2,去掉的石头的编号就为:2,4,9 cnt = 3(cnt<M了,
当然也有可能cnt == M,总之就是cnt > M不成立了)
也就是说,之后求得的cnt <= M恒成立, 即题目的答案不在 l 和 h 之间了(这点
之前是让我很费解的地方),更准确地说,答案就是执行这次h-1之前的h值。
若cnt <= M,则l = l + 1,讨论一下:若cnt < M,明显当前的mid小了,l = l + 1;若cnt == M,则去掉cnt块石头后,
剩下的石头的最小值必然是大于mid的,所以进行操作l = l + 1。
然后解决上面遇到的问题,因为h已经小于答案了,而答案就是h+1,之后继续二分cnt <= M恒成立,
l 不断自增,直到跳出循环,因为答案为h+1,我们返回 l 的值,那么while的判断条件就是 l <= h,
当 l 自增到 h + 1时就跳出循环,l == h + 1,正好就是答案。
写了这么多,就是分析了一下二分算法执行的过程,因为以前用的二分while判断条件都是 l < h,看样子以后做二分得多注意了,
稍不注意就会有很多致命的小bug,一定要将对 l 和 h 的操作以及 while的判断条件结合起来考虑,要对二分算法进行灵活的变化,
没有一成不变的模版,具体问题具体分析。
【代码】
poj 3258
牛要到河对岸,在与河岸垂直的一条线上,河中有N块石头,给定河岸宽度L,以及每一块石头离牛所在河岸的距离,
现在去掉M块石头,要求去掉M块石头后,剩下的石头之间以及石头与河岸的最小距离的最大值。
【解法】
用二分做,但是开始写了三个版本的二分,全都wa。
无赖看了别人的二分,还是不理解,为什么他们写的就能过。
反复思索后,终于明白了:关键在于题目求的是什么。
做题思想:二分所求的最小距离的最大值mid,记录可以去掉的石头块数cnt(注意:当相邻的石头的距离小于等于mid,就可以去掉),
若cnt > M,h = h - 1 (这里是比较难理解的,也是我纠结挺久的地方),此时,应当回过头来看一下题目求的是什么:
寻找一个长度mid,使得可以去掉M块石头,剩下的石头中,石头间的最小距离为mid。
但是cnt记录的是:相邻距离小于等于mid的块数,所以存在这样的一种情况 ---- cnt记录的所有石头中,
有很多块石头间的距离是等于mid,而距离小于mid的石头的块数是小于等于M的,此时,若将h的值减一,
那么h的值就变成一个小于题目所求的答案了。
举个例子就懂了:假设有9块石头,首尾的数都表示河岸。
石头的编号 1 2 3 4 5 6 7 8 9
石头到河岸的距离 0 4 5 7 9 12 16 19 23 26 28
相邻的距离 4 1 2 2 3 4 3 3 3 2
假设l = 1,h = 5, M = 4 则mid = 3, 此时去掉的石头的编号为:2,3,5,7,9 cnt = 5
按照程序,h = h - 1 = 4,此时mid = 2,去掉的石头的编号就为:2,4,9 cnt = 3(cnt<M了,
当然也有可能cnt == M,总之就是cnt > M不成立了)
也就是说,之后求得的cnt <= M恒成立, 即题目的答案不在 l 和 h 之间了(这点
之前是让我很费解的地方),更准确地说,答案就是执行这次h-1之前的h值。
若cnt <= M,则l = l + 1,讨论一下:若cnt < M,明显当前的mid小了,l = l + 1;若cnt == M,则去掉cnt块石头后,
剩下的石头的最小值必然是大于mid的,所以进行操作l = l + 1。
然后解决上面遇到的问题,因为h已经小于答案了,而答案就是h+1,之后继续二分cnt <= M恒成立,
l 不断自增,直到跳出循环,因为答案为h+1,我们返回 l 的值,那么while的判断条件就是 l <= h,
当 l 自增到 h + 1时就跳出循环,l == h + 1,正好就是答案。
写了这么多,就是分析了一下二分算法执行的过程,因为以前用的二分while判断条件都是 l < h,看样子以后做二分得多注意了,
稍不注意就会有很多致命的小bug,一定要将对 l 和 h 的操作以及 while的判断条件结合起来考虑,要对二分算法进行灵活的变化,
没有一成不变的模版,具体问题具体分析。
【代码】
/* Problem:poj 3258 By:S.B.S. /* #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<cstdlib> #include<iomanip> #include<cassert> #include<climits> #define maxn 10001 #define F(i,j,k) for(int i=j;i<=k;i++) #define M(a,b) memset(a,b,sizeof(a)) #define FF(i,j,k) for(int i=j;i>=k;i--) #define inf 0x7fffffff #define p 23333333333333333 using namespace std; int l,n,m; int d[50005]; int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline int run(int le,int h,int k) { int mid,last,cnt; while(le<=h) { mid=(le+h)>>1; last=cnt=0; F(i,1,n+1){ if(mid>=d[i]-d[last]) cnt++; else last=i; } if(cnt>k) h=mid-1; else le=mid+1; } return le; } bool cmp(int a,int b) { return a<b; } int main() { std::ios::sync_with_stdio(false);//cout<<setiosflags(ios::fixed)<<setprecision(1)<<y; // freopen("data.in","r",stdin); // freopen("data.out","w",stdout); cin>>l>>n>>m; d[0]=0; d[n+1]=l; F(i,1,n){ cin>>d[i]; } sort(d+1,d+1+n,cmp); cout<<run(0,l,m)<<endl; return 0; }
poj 3258
相关文章推荐
- opengl 贴图纹理
- 架构设计:系统间通信(38)——Apache Camel快速入门(下1)
- 每天一个linux命令(33)--du命令
- Linux下读取默认MAC地址
- 红帽、微软和 Codenvy 联合推出语言服务器协定(Language Server Protocol,LSP)项目
- Detach Volume 操作 - 每天5分钟玩转 OpenStack(55)
- Detach Volume 操作 - 每天5分钟玩转 OpenStack(55)
- Detach Volume 操作 - 每天5分钟玩转 OpenStack(55)
- Tomcat部署Web应用方法总结
- OpenCV 鼠标手动绘制掩码图像
- OpenCV 鼠标手动绘制掩码图像
- 揭开Linux操作系统的Swap交换区之谜
- 揭开Linux操作系统的Swap交换区之谜
- 高效Linux用户需要了解的命令行技能
- 高效Linux用户需要了解的命令行技能
- bootchart--检测linux启动性能的软件
- bootchart--检测linux启动性能的软件
- 掌握 Linux 调试技术
- 掌握 Linux 调试技术
- linux虚拟机拓展大小