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;
}