UVALive 3610 Log Jumping(贪心)

来源:互联网 发布:数据结构与算法分析pdf 编辑:程序博客网 时间:2024/06/05 08:19

题目链接:点击打开链接

刘汝佳的大白书里面对这道题的归类是可转化为经典问题的DP,但是死活想不出来怎么dp。

反倒是想到了一种类似贪心的思路。

首先,我们可以把跳的路线看做是一个环,并把跳跃的顺序强制规定为【从最左边往右跳到最右边再往左跳】。

初步思路是对木头的左端点排序,然后枚举最左的木头,之后维护上下两个端点(木头)进行dp,就跟双调欧几里得旅行商问题一样。

但是这么做的话时间复杂度为O(n^3),显然不行。

后来发现完全没有这个必要。当你知道最左的木头的时候其实就可以由贪心的策略得到其最长的长度了。

首先当然也是排序,对木头的左端点升序排序。

对于已确定的左端点,分两种情况:

1. 它跳不到下一条木头,那么答案就为1。

2. 它跳的到下一条木头,那么这条木头就默认作为在维护的【环】中的上端点,原来那个就是下端点。于是就可以继续往下扫过去,每条被扫到的木头都放到跟上下端点中的最左的那条相接的位置【也就是说,由上下端点中的最左的那条跳往这一条】,并更新新的端点。

而对于第二步,由于每条木条的长度是一样的,也就是说当上端点被更新之后必定是下端点最左,反之就是上端点最左。

于是我们就可以默认在这种情况下第i条必定由第i-2条跳过去。

枚举然后扫一边就行了,复杂度O(n^2)。

代码如下:

#include<bits/stdc++.h>#define INF 10000000using namespace std;int a[5005];int n,len;inline bool check(int i,int j){return a[i]+len>=a[j];}int main(){int t;scanf("%d",&t);while(t--){scanf("%d%d",&n,&len);for(int i=0;i<n;i++)scanf("%d",&a[i]);sort(a,a+n);int ans=1;for(int i=0;i<n;i++){if(n-i<=ans)break;int pans=1;if(check(i,i+1))pans++;else continue;for(int j=i+2;j<n;j++){if(check(j-2,j))pans++;else break;}ans=max(ans,pans);}printf("%d\n",ans);}}


0 0