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

    P3957 跳房子

    祭奠一下逝去的时间


    题解

    50pt Solution

    看到这个题直觉想到是DP 但是不会写

    我们可以二分答案,花费金币为 (mid) ,二分枚举 (mid)

    我们设置 (f[i]) 为到达第 (i) 个节点时的最大得分,由于跳房子是一直往右跳的,所以 (f[i]) 一定是由它之前的节点中得分最大的转移过来的,所以有:

    for(int i=1;i<=n;i++)
    	  for(int j=i-1;j>=0;j--){
    	  	if(x[i]-x[j]>=lef&&x[i]-x[j]<=rig)
    	  	   f[i]=max(f[i],f[j]+s[i]);
            if(f[i]>k) return 1;
    	  }
    

    但是这题数据太大了。。。(n^2) 只能 (50pt)

    (code)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<string>
    #include<cstring>
    #include<queue>
    using namespace std;
    
    typedef long long ll;
    
    inline int read()
    {
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch<'0'||ch>'9') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    const int maxn=5e5+10,inf=-1e9+10;
    int n,d,k;
    int s[maxn],x[maxn];
    int l=0,r=0,mid=0;
    ll f[maxn];
    int ans;
    
    bool check(int mid)
    {
    	for(int i=1;i<=n;i++) f[i]=inf;
    	int lef=max(1,d-mid);
    	int rig=d+mid;
    	for(int i=1;i<=n;i++)
    	  for(int j=i-1;j>=0;j--){
    	  	if(x[i]-x[j]<lef) continue;
    	  	if(x[i]-x[j]>rig) break;
    	  	f[i]=max(f[i],f[j]+s[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();s[i]=read();
    		if(s[i]>0) ans+=s[i];
    		r=max(r,x[i]);
    	}
    	if(ans<k){
    		printf("-1
    ");
    		return 0;
    	}
    	while(l<r){
    		mid=(l+r)>>1;
    		if(check(mid)) r=mid;
    		else l=mid+1;
    	}
    	printf("%d
    ",r);
    	
    	return 0;
    }
    

    100pt Solution

    满分要二分+DP+单调队列

    我们又上面可知

    (f[ i ]=max( f[j] )+s[i] ,j<i)

    而且随着 (i) 不断往前跳,向前更新,(j) 一定会有不在弹跳范围内的,所以由单调性可知可以使用单调队列

    队列队首维护使得 $f[i] $ 最大的那个点下标

    我们枚举 (j) ,只需要枚举一遍就行了,(j) 是那个待加入的点的下标,如果它在弹跳范围内,而且下标小于 (i) ,就把它加入到队列中的合适位置,加入之后仍然保持队列单调性,弹出超出合法弹跳区间的点,如果队列非空就更新 $f[i] $ 的值

    主体部分:

    bool check(ll mid)
    {
    	q.clear() ;
    	for(int i=1;i<=n;i++) f[i]=inf;f[0]=0;
    	ll lef=(d-mid<0?1:d-mid);
    	ll rig=d+mid;
    	int j=0;
    	for(int i=1;i<=n;i++){
    		while(x[i]-x[j]>rig) j++;
    		while(x[i]-x[j]<=rig&&x[i]-x[j]>=lef&&j<i){
    			while(!q.empty() &&(f[j]>=f[q.back() ]||x[i]-x[q.back()]>rig)) q.pop_back() ;
    			q.push_back(j) ;
    			j++;
    		}
    		while(!q.empty() &&x[i]-x[q.front() ]>rig) q.pop_front() ;
    		if(!q.empty()) f[i]=f[q.front()]+s[i];
    		if(f[i]>=k) return 1;
    	}
    	return 0;
    }
    
    

    (code)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<string>
    #include<cstring>
    #include<queue>
    using namespace std;
    
    typedef long long ll;
    
    inline ll read()
    {
    	ll ans=0;
    	char last=' ',ch=getchar();
    	while(ch<'0'||ch>'9') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    const int maxn=5e5+10;
    const ll inf=-1e18+10;
    int n;
    ll k,d;
    ll s[maxn],x[maxn];
    ll l=0,r=0,mid=0;
    ll f[maxn];
    deque<ll>q;
    
    bool check(ll mid)
    {
    	q.clear() ;
    	for(int i=1;i<=n;i++) f[i]=inf;f[0]=0;
    	ll lef=(d-mid<0?1:d-mid);
    	ll rig=d+mid;
    	int j=0;
    	for(int i=1;i<=n;i++){
    		while(x[i]-x[j]>rig) j++;
    		while(x[i]-x[j]<=rig&&x[i]-x[j]>=lef&&j<i){
    			while(!q.empty() &&(f[j]>=f[q.back() ]||x[i]-x[q.back()]>rig)) q.pop_back() ;
    			q.push_back(j) ;
    			j++;
    		}
    		while(!q.empty() &&x[i]-x[q.front() ]>rig) q.pop_front() ;
    		if(!q.empty()) f[i]=f[q.front()]+s[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(),s[i]=read();
        l=0,r=x[n];
        while(l<r){
        	mid=(l+r)>>1;
        	if(check(mid)) r=mid;
        	else l=mid+1;
    	}
    	if(!check(l)) {
    		printf("-1
    ");
    		return 0;
    	}
    	printf("%lld
    ",l);
    	return 0;
    }
    
  • 相关阅读:
    HDU 5642 King's Order 动态规划
    HDU 5640 King's Cake GCD
    HDU 5641 King's Phone 模拟
    HDU 5299 Circles Game 博弈论 暴力
    HDU 5294 Tricks Device 网络流 最短路
    HDU 5289 Assignment rmq
    HDU 5288 OO’s Sequence 水题
    星际争霸 虚空之遗 人族5BB 操作流程
    Codeforces Beta Round #3 D. Least Cost Bracket Sequence 优先队列
    Codeforces Beta Round #3 C. Tic-tac-toe 模拟题
  • 原文地址:https://www.cnblogs.com/xiaoyezi-wink/p/12152901.html
Copyright © 2011-2022 走看看