50分做法
挺显然的一个做法,因为金币量是单调的(如果你花i枚金币可以得到最优解,i+1枚也一定可以),所以可以二分答案
然后对于二分出来的每个答案,都做一遍dp,效率$O(n^2logn)$
#include <cstdio>
#include <cstring>
using namespace std;
#define N 500100
inline int read(){
int x=,f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-f;c=getchar();}
while(c>=''&&c<=''){x=(x<<)+(x<<)+c-'';c=getchar();}
return x*f;
}
int n,d,k,a[N],s[N],dp[N];
int max(int x,int y){return x>y?x:y;}
bool check(int g){
memset(dp,,sizeof(dp));
int t1=(d-g)?d-g:,t2=d+g,mx=;
dp[]=;
for(int i=;i<=n;i++){
for(int j=;j<i;j++){
if(a[i]-a[j]>=t1&&a[i]-a[j]<=t2)
dp[i]=max(dp[i],dp[j]+s[i]);
}
}
for(int i=;i<=n;i++)mx=max(dp[i],mx);
if(mx>=k)return ;
return ;
}
int main(){
n=read();d=read();k=read();
for(int i=;i<=n;i++)
a[i]=read(),s[i]=read();
int ans=,l=,r=;
while(l<=r){
int mid=(l+r)>>;
if(check(mid))r=mid-,ans=mid;
else l=mid+;
}
if(ans)printf("%d\n",ans);
else puts("-1");
return ;
}
50分做法
100分做法
考虑怎么让效率降下来
50分的思路没问题,尝试一下能不能让每次dp的效率降下来
观察到答案其实也是单调的,dp[i]的答案是从前面i-d-g个数转移过来的,所以可以使用单调队列优化
总复杂度就变成$O(nlogn)$,能过100分的数据了
#include <cstdio>
#include <cstring>
#define ll long long
inline ll read(){
ll x=,f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-f;c=getchar();}
while(c>=''&&c<=''){x=(x<<)+(x<<)+c-'';c=getchar();}
return x*f;
}
using namespace std;
#define inf (1<<30)
ll n,d,k,a[],s[];
ll dp[];
ll q[];
ll max(ll x,ll y){return x>y?x:y;}
bool check(ll g){
ll l=,r=,p=,t1=max(d-g,),t2=d+g;
q[]=;
for(int i=;i<=n;i++){
dp[i]=-inf;
while(a[i]-a[p]>=t1&&p<i){
while(l<=r&&dp[p]>=dp[q[r]])r--;
q[++r]=p++;
}
while(a[i]-a[q[l]]>t2&&l<=r)l++;
if(l>r||dp[q[l]]==-inf)continue;
dp[i]=dp[q[l]]+s[i];
if(dp[i]>=k)return ;
}
return ;
}
int main(){
n=read(),d=read(),k=read();
for(int i=;i<=n;i++)
a[i]=read(),s[i]=read();
a[]=,s[]=;
ll l=,r=,ans=-;
while(l<=r){
ll mid=(l+r)>>;
if(check(mid))ans=mid,r=mid-;
else l=mid+;
}
printf("%lld\n",ans);
return ;
}
100分做法