NOIP2017 跳房子(单调队列优化DP+二分)

限于喜欢 提交于 2019-12-01 12:55:32

50分做法:

可以发现答案具有单调性,所以二分答案。判断是否可行就需要DP。dp[i]表示到第i个格能得的最大值,从能跳向它的格进行转移即可。

#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxm=500007;
int n,d,k;
struct node
{
 int x,z;
}a[maxm];
int ans=-1;
ll dp[maxm];
bool check(int x)
{
  int maxx,minn;
  if(x<d)
  minn=d-x;
  else minn=1;
  maxx=d+x;
  memset(dp,-0x3f,sizeof(dp));
  dp[0]=0;
  for(int i=1;i<=n;i++)
  {
    for(int j=0;j<=i-1;j++)
    {
     if((a[i].x-a[j].x)<minn||(a[i].x-a[j].x)>maxx) continue;
     dp[i]=max(dp[i],dp[j]+a[j].z); 
    }
  }
  ll tot=-1e15;
  for(int i=0;i<=n;i++)
  {
     tot=max(tot,dp[i]);
  }
  if(tot>=k) return 1;
  else return 0;
}
int main()
{
 scanf("%d%d%d",&n,&d,&k);
 for(int i=1;i<=n;i++)
 scanf("%d%d",&a[i].x,&a[i].z);
 int l=0,r=a[n].x;
 while(l<=r)
 {
  int mid=(l+r)>>1;
  if(check(mid))
  {
    ans=mid;
    r=mid-1;
  }
  else
  l=mid+1; 
 }
 printf("%d\n",ans);
 return 0;
}

满分做法:

50分做法的转移是O(\(n\sqrt{n}\))的,如何更快的转移呢?因为随着格的转移,能被跳到的区间也在转移,而且他一定是从这个区间中的最大值转移过来。

嗯!单调队列即可。

#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxm=500007;
int n,d,k;
struct node
{
 int x,z;
}a[maxm];
int ans=-1;
ll dp[maxm];
int q[maxm];
bool check(int x)
{
  int maxx,minn;
  if(x<d)
  minn=d-x;
  else minn=1;
  maxx=d+x;
  memset(dp,-0x3f,sizeof(dp));
  dp[0]=0;
  int j=0;
  int h=1,t=0;
  for(int i=1;i<=n;i++)
  {
    for(;a[j].x+minn<=a[i].x;j++)
    {
      while(h<=t&&dp[q[t]]<=dp[j]) t--;//维护一个单调递减的队列 
      q[++t]=j; 
    }
    while(h<=t&&a[q[h]].x+maxx<a[i].x) h++;
    if(h<=t) dp[i]=dp[q[h]]+a[i].z;
    if(dp[i]>=k) return 1;
  }
  return 0;
}
int main()
{
 scanf("%d%d%d",&n,&d,&k);
 for(int i=1;i<=n;i++)
 scanf("%d%d",&a[i].x,&a[i].z);
 int l=0,r=a[n].x;
 while(l<=r)
 {
  int mid=(l+r)>>1;
  if(check(mid))
  {
    ans=mid;
    r=mid-1;
  }
  else
  l=mid+1; 
 }
 printf("%d\n",ans);
 return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!