zoukankan      html  css  js  c++  java
  • 跳房子

    洛咕

    题意:在地面上确定一个起点,然后在起点右侧画(n(n<=500000))个格子,这些格子都在同一条直线上.每个格子内有一个数字(整数),表示到达这个 格子能得到的分数.玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内.第二次再从当前位置继续向右跳,依此类推.规则规定:玩家每次都必须跳到当前位置右侧的一个格子内.玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和.每一次可以跳的固定距离为(d),然后花费(g)元,可以跳的区间范围为(max(d-g,1),d+g),求最少花费多少钱可以获得至少(k)分?

    答案显然具有单调性,所以可以二分最小花费为(mid),得到可以跳的区间范围为([max(d-mid,1),d+mid]).

    (f[i])表示跳到第(i)个格子能够获得的最大分数,(f[i]=f[j]+val[i]),其中((max(d-mid,1)<=dis(i-j)<=d+mid)),(val[i])表示第i格的得分.

    这个(j)可以(n^2)枚举(50分),也可以单调队列维护.但是本题的单调队列需要用到双端队列(deque),因为(j)既有上界又有下界.

    时间复杂度(O(nlog_{x_n}).)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=500005;
    int n,d,k,x[N],val[N];ll sum,f[N];
    inline bool check(int mid){
    	for(int i=0;i<=n;++i)f[i]=-1e18;f[0]=0;
    //这里一定要赋值为负无穷,因为格子的分数可能是负的
    //没有的话,只有50分
    	int minn=max(d-mid,1),maxn=d+mid;//跳的上下界
    	int j=0;deque<int>q;//在里面建立队列就不用初始化了
    	for(int i=1;i<=n;++i){
    		while(j<i&&x[i]-x[j]>=minn){
    			while(q.size()&&f[j]>=f[q.back()])q.pop_back();
    			q.push_back(j);++j;
    		}//把符合条件的j加进队列里面,并维护单调性
    		while(q.size()&&x[i]-x[q.front()]>maxn)q.pop_front();//队列前面也要维护
    		if(q.size())f[i]=f[q.front()]+val[i];//更新
    		if(f[i]>=k)return 1;//判断
    	}
    	return 0;
        
    	/*
        暴力的n方做法
        for(int i=0;i<=n;++i)f[i]=0;
    	int minn=max(d-mid,1),maxn=d+mid;
    	for(int i=1;i<=n;++i){
    		for(int j=0;j<i;++j){
    			if(x[i]-x[j]>=minn&&x[i]-x[j]<=maxn)
    				f[i]=max(f[i],f[j]+val[i]);
    		}
    		if(f[i]>=k)return 1;
    	}
    	return 0;
        */
    }
    int main(){
    	n=read();d=read();k=read();
    	for(int i=1;i<=n;++i){
    		x[i]=read();val[i]=read();
    		if(val[i]>0)sum+=val[i];
    	}
    	if(sum<k){puts("-1");return 0;}//特判
    	int l=0,r=x[n],ans,mid;//最大花费就是最远的那个格子的距离
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(check(mid)){
    			ans=mid;
    			r=mid-1;
    		}
    		else l=mid+1;
    	}
    	printf("%d
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    [转]创建节约内存的JavaBean
    TCP协议
    redis常用命令
    Windows&Linux常用命令笔记
    二叉树学习笔记
    云计算入门
    redis主从切换
    spring 异步处理request
    ibatis工作原理
    工作了3年的JAVA程序员应该具备什么技能?(转)
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11599972.html
Copyright © 2011-2022 走看看