题目链接

设$dp[l][r][p]$为走完区间$[l,r]$,在端点$p$时所需的最短时间($p=0$代表在左端点,$p=1$代表在右端点)

根据题意显然有状态转移方程$\left\{\begin{matrix}dp[l][r][0]=min(dp[l+1][r][0]+x[l+1]-x[l],dp[l+1][r][1]+x[r]-x[l]);\\ dp[l][r][1]=min(dp[l][r-1][0]+x[r]-x[l],dp[l][r-1][1]+x[r]-x[r-1]);\end{matrix}\right.$

复杂度是$O(n^2)$的,对于10000的数据量还是有些勉强,因此某些常数比较大的算法会T掉。

为此我测试了多种不同的算法,比较了一下它们各自的优劣。

首先是最普通的区间dp:(440ms)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=+,inf=0x3f3f3f3f;
  5. int dp[N][N][],n,x[N],t[N],ans;
  6.  
  7. int main() {
  8. while(scanf("%d",&n)==) {
  9. for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
  10. for(int i=; i<n; ++i)dp[i][i][]=dp[i][i][]=;
  11. for(int l=n-; l>=; --l)
  12. for(int r=l+; r<n; ++r) {
  13. dp[l][r][]=min(dp[l+][r][]+x[l+]-x[l],dp[l+][r][]+x[r]-x[l]);
  14. dp[l][r][]=min(dp[l][r-][]+x[r]-x[l],dp[l][r-][]+x[r]-x[r-]);
  15. if(dp[l][r][]>=t[l])dp[l][r][]=inf;
  16. if(dp[l][r][]>=t[r])dp[l][r][]=inf;
  17. }
  18. int ans=min(dp[][n-][],dp[][n-][]);
  19. if(ans==inf)printf("No solution\n");
  20. else printf("%d\n",ans);
  21. }
  22. return ;
  23. }

注意l要从右往左循环就行。

然后是用滚动数组优化后的dp:(290ms)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=+,inf=0x3f3f3f3f;
  5. int dp[][N][],n,x[N],t[N],ans;
  6.  
  7. int main() {
  8. while(scanf("%d",&n)==) {
  9. for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
  10. memset(dp,,sizeof dp);
  11. for(int l=n-; l>=; --l)
  12. for(int r=l+; r<n; ++r) {
  13. dp[l&][r][]=min(dp[l&^][r][]+x[l+]-x[l],dp[l&^][r][]+x[r]-x[l]);
  14. dp[l&][r][]=min(dp[l&][r-][]+x[r]-x[l],dp[l&][r-][]+x[r]-x[r-]);
  15. if(dp[l&][r][]>=t[l])dp[l&][r][]=inf;
  16. if(dp[l&][r][]>=t[r])dp[l&][r][]=inf;
  17. }
  18. int ans=min(dp[][n-][],dp[][n-][]);
  19. if(ans==inf)printf("No solution\n");
  20. else printf("%d\n",ans);
  21. }
  22. return ;
  23. }

和上面的代码唯一的不同之处就是l换成了l&1,l+1换成了l&1^1。

其他算法:

bfs刷表法:(1940ms)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=+,inf=0x3f3f3f3f;
  5. int dp[][N][],vis[][N][],n,x[N],t[N],ans,ka;
  6. struct D {int l,r,p;};
  7. queue<D> q;
  8.  
  9. void upd(int l,int r,int p,int ad) {
  10. if(vis[l&][r][p]!=l)vis[l&][r][p]=l,dp[l&][r][p]=inf,q.push({l,r,p});
  11. if(dp[l&][r][p]>ad)dp[l&][r][p]=ad;
  12. }
  13.  
  14. int bfs() {
  15. memset(vis,-,sizeof vis);
  16. int ret=inf;
  17. while(!q.empty())q.pop();
  18. for(int i=; i<n; ++i)upd(i,i,,);
  19. while(!q.empty()) {
  20. int l=q.front().l,r=q.front().r,p=q.front().p;
  21. q.pop();
  22. if(l==&&r==n-) {ret=min(ret,dp[l&][r][p]); continue;}
  23. int P=p==?l:r;
  24. if(l>&&dp[l&][r][p]+x[P]-x[l-]<t[l-])upd(l-,r,,dp[l&][r][p]+x[P]-x[l-]);
  25. if(r<n-&&dp[l&][r][p]+x[r+]-x[P]<t[r+])upd(l,r+,,dp[l&][r][p]+x[r+]-x[P]);
  26. }
  27. return ret;
  28. }
  29.  
  30. int main() {
  31. while(scanf("%d",&n)==) {
  32. ++ka;
  33. for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
  34. int ans=bfs();
  35. if(ans==inf)printf("No solution\n");
  36. else printf("%d\n",ans);
  37. }
  38. return ;
  39. }

这个算法对于本题来说常数比较大,必须要用滚动数组优化。

dfs记忆化搜索法:(1580ms)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=+,inf=0x3f3f3f3f;
  5. int dp[N][N][],vis[N][N],n,x[N],t[N],ans,ka;
  6.  
  7. void dfs(int l,int r) {
  8. if(vis[l][r]==ka)return;
  9. vis[l][r]=ka;
  10. if(l==r)dp[l][r][]=dp[l][r][]=;
  11. else {
  12. dfs(l+,r),dfs(l,r-);
  13. dp[l][r][]=min(dp[l+][r][]+x[l+]-x[l],dp[l+][r][]+x[r]-x[l]);
  14. dp[l][r][]=min(dp[l][r-][]+x[r]-x[l],dp[l][r-][]+x[r]-x[r-]);
  15. if(dp[l][r][]>=t[l])dp[l][r][]=inf;
  16. if(dp[l][r][]>=t[r])dp[l][r][]=inf;
  17. }
  18. }
  19.  
  20. int main() {
  21. while(scanf("%d",&n)==) {
  22. ++ka;
  23. for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
  24. dfs(,n-);
  25. int ans=min(dp[][n-][],dp[][n-][]);
  26. if(ans==inf)printf("No solution\n");
  27. else printf("%d\n",ans);
  28. }
  29. return ;
  30. }

这个算法常数也比较大,但无法用滚动数组来优化,因此需要额外增设一个vis数组和一个变量ka,来避免dp数组的初始化。

去掉一维直接dp法:(260ms)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=+,inf=0x3f3f3f3f;
  5. int dp[N][],n,x[N],t[N],ans;
  6.  
  7. int main() {
  8. while(scanf("%d",&n)==) {
  9. for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
  10. memset(dp,,sizeof dp);
  11. for(int i=; i<n; ++i)
  12. for(int l=; l+i<n; ++l) {
  13. dp[l][]=min(dp[l][]+x[l+i]-x[l],dp[l][]+x[l+i]-x[l+i-]);
  14. dp[l][]=min(dp[l+][]+x[l+]-x[l],dp[l+][]+x[l+i]-x[l]);
  15. if(dp[l][]>=t[l])dp[l][]=inf;
  16. if(dp[l][]>=t[l+i])dp[l][]=inf;
  17. }
  18. int ans=min(dp[][],dp[][]);
  19. if(ans==inf)printf("No solution\n");
  20. else printf("%d\n",ans);
  21. }
  22. return ;
  23. }

这个算法无论是时间还是空间的消耗都是最小的,虽然比较抽象。设dp[i][l][p]为区间左端点为l,长度为i+1,当前位置为p时所需的最短时间,则第一维i可以直接去掉。

UVA - 1632 Alibaba (区间dp+常数优化)的更多相关文章

  1. UVA - 1632 Alibaba 区间dp

    题意:给定n个点,其中第i个点的坐标是,且它会在秒后消失.Alibaba可以从任意位置出发,求访问完所有点的最短时间.无解输出No solution. 思路:表示访问完区间后停留在i点的最短时间,表示 ...

  2. HDU3480_区间DP平行四边形优化

    HDU3480_区间DP平行四边形优化 做到现在能一眼看出来是区间DP的问题了 也能够知道dp[i][j]表示前  i  个节点被分为  j  个区间所取得的最优值的情况 cost[i][j]表示从i ...

  3. POJ 1160 经典区间dp/四边形优化

    链接http://poj.org/problem?id=1160 很好的一个题,涉及到了以前老师说过的一个题目,可惜没往那上面想. 题意,给出N个城镇的地址,他们在一条直线上,现在要选择P个城镇建立邮 ...

  4. codeforces 1101F Trucks and Cities 区间dp+单调优化 好题

    题目传送门 题意简述:(来自洛谷) 有n个城市坐落在一条数轴上,第ii个城市位于位置ai​. 城市之间有m辆卡车穿行.每辆卡车有四个参数:si​为起点编号,fi​为终点编号,ci​表示每行驶1个单位长 ...

  5. 蓝桥杯:合并石子(区间DP+平行四边形优化)

    http://lx.lanqiao.cn/problem.page?gpid=T414 题意:…… 思路:很普通的区间DP,但是因为n<=1000,所以O(n^3)只能拿90分.上网查了下了解了 ...

  6. 51 nod 石子归并 + v2 + v3(区间dp,区间dp+平行四边形优化,GarsiaWachs算法)

    题意:就是求石子归并. 题解:当范围在100左右是可以之间简单的区间dp,如果范围在1000左右就要考虑用平行四边形优化. 就是多加一个p[i][j]表示在i到j内的取最优解的位置k,注意能使用平行四 ...

  7. 区间dp及优化

    看了下感觉区间dp就是一种套路,直接上的板子代码就好了. 基础题ac代码:石子归并 #include<bits/stdc++.h> using namespace std; typedef ...

  8. hdu 4597 + uva 10891(一类区间dp)

    题目链接:http://vjudge.net/problem/viewProblem.action?id=19461 思路:一类经典的博弈类区间dp,我们令dp[l][r]表示玩家A从区间[l, r] ...

  9. uva 1632 Alibaba

    题意: 一个人要从如果干个地方拿货,每个地方的货物是有存在时间的,到了某个时间之后就会消失. 按照位置从左到右给出货物的位置以及生存时间,这个人选择一个最优的位置出发,问拿完货物的最少时间. 思路: ...

随机推荐

  1. LeetCode:二叉树的锯齿形层次遍历【103】

    LeetCode:二叉树的锯齿形层次遍历[103] 题目描述 给定一个二叉树,返回其节点值的锯齿形层次遍历.(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行). 例如:给定二叉树 ...

  2. dockfile

    dockerfile是对镜像的描述 新建一个dockfile文件 docker inspect

  3. storage

    localStorage(本地存储),可以长期存储数据,没有时间限制,一天,一年,两年甚至更长,数据都可以使用. sessionStorage(会话存储),只有在浏览器被关闭之前使用,创建另一个页面时 ...

  4. Loadrunder之脚本篇——参数化取值策略

    参数取值选项 Select next row Update value on 以上两个选项是改变参数化取值的关键选项. Select next row包含如下选项: Sequential:顺序选择 R ...

  5. OC知识点(类方法,构造方法,组合模式,get,set方法,自动生成属性)

    1.类方法的优势 不用创建对象,节省了空间,直接用类名调用类方法,类方法为外界提供一个方便的调用接口.(特点:类方法以加号开头,不能使用自身的成员变量,它的调用不依赖成员变量) 2.构造方法(初始化成 ...

  6. PHP类的变量与成员,及其继承、访问与重写要注意的问题

    PHP的类及其实例: <?php ?>  后期静态绑定:为了避免子类重写静态属性后,使用继承来的方法仍然方法父类的静态属性,PHP5.3增加了一个新的语法,后期静态绑定,使用static关 ...

  7. Android编译系统环境过程初始化分析【转】

    本文转载自:http://blog.csdn.net/luoshengyang/article/details/18928789 Android源代码在编译之前,要先对编译环境进行初始化,其中最主要就 ...

  8. JSP 导出Excel表格

    ES6语法 传入一个table的id,然后在导出excel按钮上加入一个<a href="#" id="buttonId">导出Excel</ ...

  9. IntelliJ IDEA 中 右键新建时,选项没有Java class的解决方法和具体解释

    我是在别人问我的时候,才发现还可以有这个问题,主要是他新项目直接打开,什么都没配置,就打算新建文件,其实可以直接看编辑器右下角的event log,那个对话框点开的话,可以add as maven p ...

  10. oracle 导出

    一.导出csv文件 利用Oracle中的Spool缓冲池技术可以实现Oracle数据导出到文本文件. 1).在Oracle PL/SQL中输入缓冲开始命令,并指定输出的文件名: spool /tmp/ ...