UVA - 1632 Alibaba (区间dp+常数优化)
设$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)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+,inf=0x3f3f3f3f;
int dp[N][N][],n,x[N],t[N],ans; int main() {
while(scanf("%d",&n)==) {
for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
for(int i=; i<n; ++i)dp[i][i][]=dp[i][i][]=;
for(int l=n-; l>=; --l)
for(int r=l+; r<n; ++r) {
dp[l][r][]=min(dp[l+][r][]+x[l+]-x[l],dp[l+][r][]+x[r]-x[l]);
dp[l][r][]=min(dp[l][r-][]+x[r]-x[l],dp[l][r-][]+x[r]-x[r-]);
if(dp[l][r][]>=t[l])dp[l][r][]=inf;
if(dp[l][r][]>=t[r])dp[l][r][]=inf;
}
int ans=min(dp[][n-][],dp[][n-][]);
if(ans==inf)printf("No solution\n");
else printf("%d\n",ans);
}
return ;
}
注意l要从右往左循环就行。
然后是用滚动数组优化后的dp:(290ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+,inf=0x3f3f3f3f;
int dp[][N][],n,x[N],t[N],ans; int main() {
while(scanf("%d",&n)==) {
for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
memset(dp,,sizeof dp);
for(int l=n-; l>=; --l)
for(int r=l+; r<n; ++r) {
dp[l&][r][]=min(dp[l&^][r][]+x[l+]-x[l],dp[l&^][r][]+x[r]-x[l]);
dp[l&][r][]=min(dp[l&][r-][]+x[r]-x[l],dp[l&][r-][]+x[r]-x[r-]);
if(dp[l&][r][]>=t[l])dp[l&][r][]=inf;
if(dp[l&][r][]>=t[r])dp[l&][r][]=inf;
}
int ans=min(dp[][n-][],dp[][n-][]);
if(ans==inf)printf("No solution\n");
else printf("%d\n",ans);
}
return ;
}
和上面的代码唯一的不同之处就是l换成了l&1,l+1换成了l&1^1。
其他算法:
bfs刷表法:(1940ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+,inf=0x3f3f3f3f;
int dp[][N][],vis[][N][],n,x[N],t[N],ans,ka;
struct D {int l,r,p;};
queue<D> q; void upd(int l,int r,int p,int ad) {
if(vis[l&][r][p]!=l)vis[l&][r][p]=l,dp[l&][r][p]=inf,q.push({l,r,p});
if(dp[l&][r][p]>ad)dp[l&][r][p]=ad;
} int bfs() {
memset(vis,-,sizeof vis);
int ret=inf;
while(!q.empty())q.pop();
for(int i=; i<n; ++i)upd(i,i,,);
while(!q.empty()) {
int l=q.front().l,r=q.front().r,p=q.front().p;
q.pop();
if(l==&&r==n-) {ret=min(ret,dp[l&][r][p]); continue;}
int P=p==?l:r;
if(l>&&dp[l&][r][p]+x[P]-x[l-]<t[l-])upd(l-,r,,dp[l&][r][p]+x[P]-x[l-]);
if(r<n-&&dp[l&][r][p]+x[r+]-x[P]<t[r+])upd(l,r+,,dp[l&][r][p]+x[r+]-x[P]);
}
return ret;
} int main() {
while(scanf("%d",&n)==) {
++ka;
for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
int ans=bfs();
if(ans==inf)printf("No solution\n");
else printf("%d\n",ans);
}
return ;
}
这个算法对于本题来说常数比较大,必须要用滚动数组优化。
dfs记忆化搜索法:(1580ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+,inf=0x3f3f3f3f;
int dp[N][N][],vis[N][N],n,x[N],t[N],ans,ka; void dfs(int l,int r) {
if(vis[l][r]==ka)return;
vis[l][r]=ka;
if(l==r)dp[l][r][]=dp[l][r][]=;
else {
dfs(l+,r),dfs(l,r-);
dp[l][r][]=min(dp[l+][r][]+x[l+]-x[l],dp[l+][r][]+x[r]-x[l]);
dp[l][r][]=min(dp[l][r-][]+x[r]-x[l],dp[l][r-][]+x[r]-x[r-]);
if(dp[l][r][]>=t[l])dp[l][r][]=inf;
if(dp[l][r][]>=t[r])dp[l][r][]=inf;
}
} int main() {
while(scanf("%d",&n)==) {
++ka;
for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
dfs(,n-);
int ans=min(dp[][n-][],dp[][n-][]);
if(ans==inf)printf("No solution\n");
else printf("%d\n",ans);
}
return ;
}
这个算法常数也比较大,但无法用滚动数组来优化,因此需要额外增设一个vis数组和一个变量ka,来避免dp数组的初始化。
去掉一维直接dp法:(260ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+,inf=0x3f3f3f3f;
int dp[N][],n,x[N],t[N],ans; int main() {
while(scanf("%d",&n)==) {
for(int i=; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
memset(dp,,sizeof dp);
for(int i=; i<n; ++i)
for(int l=; l+i<n; ++l) {
dp[l][]=min(dp[l][]+x[l+i]-x[l],dp[l][]+x[l+i]-x[l+i-]);
dp[l][]=min(dp[l+][]+x[l+]-x[l],dp[l+][]+x[l+i]-x[l]);
if(dp[l][]>=t[l])dp[l][]=inf;
if(dp[l][]>=t[l+i])dp[l][]=inf;
}
int ans=min(dp[][],dp[][]);
if(ans==inf)printf("No solution\n");
else printf("%d\n",ans);
}
return ;
}
这个算法无论是时间还是空间的消耗都是最小的,虽然比较抽象。设dp[i][l][p]为区间左端点为l,长度为i+1,当前位置为p时所需的最短时间,则第一维i可以直接去掉。
UVA - 1632 Alibaba (区间dp+常数优化)的更多相关文章
- UVA - 1632 Alibaba 区间dp
题意:给定n个点,其中第i个点的坐标是,且它会在秒后消失.Alibaba可以从任意位置出发,求访问完所有点的最短时间.无解输出No solution. 思路:表示访问完区间后停留在i点的最短时间,表示 ...
- HDU3480_区间DP平行四边形优化
HDU3480_区间DP平行四边形优化 做到现在能一眼看出来是区间DP的问题了 也能够知道dp[i][j]表示前 i 个节点被分为 j 个区间所取得的最优值的情况 cost[i][j]表示从i ...
- POJ 1160 经典区间dp/四边形优化
链接http://poj.org/problem?id=1160 很好的一个题,涉及到了以前老师说过的一个题目,可惜没往那上面想. 题意,给出N个城镇的地址,他们在一条直线上,现在要选择P个城镇建立邮 ...
- codeforces 1101F Trucks and Cities 区间dp+单调优化 好题
题目传送门 题意简述:(来自洛谷) 有n个城市坐落在一条数轴上,第ii个城市位于位置ai. 城市之间有m辆卡车穿行.每辆卡车有四个参数:si为起点编号,fi为终点编号,ci表示每行驶1个单位长 ...
- 蓝桥杯:合并石子(区间DP+平行四边形优化)
http://lx.lanqiao.cn/problem.page?gpid=T414 题意:…… 思路:很普通的区间DP,但是因为n<=1000,所以O(n^3)只能拿90分.上网查了下了解了 ...
- 51 nod 石子归并 + v2 + v3(区间dp,区间dp+平行四边形优化,GarsiaWachs算法)
题意:就是求石子归并. 题解:当范围在100左右是可以之间简单的区间dp,如果范围在1000左右就要考虑用平行四边形优化. 就是多加一个p[i][j]表示在i到j内的取最优解的位置k,注意能使用平行四 ...
- 区间dp及优化
看了下感觉区间dp就是一种套路,直接上的板子代码就好了. 基础题ac代码:石子归并 #include<bits/stdc++.h> using namespace std; typedef ...
- hdu 4597 + uva 10891(一类区间dp)
题目链接:http://vjudge.net/problem/viewProblem.action?id=19461 思路:一类经典的博弈类区间dp,我们令dp[l][r]表示玩家A从区间[l, r] ...
- uva 1632 Alibaba
题意: 一个人要从如果干个地方拿货,每个地方的货物是有存在时间的,到了某个时间之后就会消失. 按照位置从左到右给出货物的位置以及生存时间,这个人选择一个最优的位置出发,问拿完货物的最少时间. 思路: ...
随机推荐
- Django——Session源码分析
首先我们导入django.contrib.sessions.middleware这个中间件,查看里面的Session源码 from django.contrib.sessions.middleware ...
- 解释python中join()和split()函数
join能让我们将指定字符添加至字符串中 a=') print(a) print(type(a)) #1,2,3,4,5,6 #<class 'str'> split()能让我们用指定字符 ...
- __init__和__new__
一.__init__方法是什么 __init__方法通常用在初始化一个类实例的时候, class Person(object): """Silly Person" ...
- Video标签的使用
现在如果要在页面中使用video标签,需要考虑三种情况,支持Ogg Theora或者VP8(如果这玩意儿没出事的话)的(Opera.Mozilla.Chrome),支持H.264的(Safari.IE ...
- echarts3.8.4实现模拟迁移
动态接受城市的经纬度https://zhidao.baidu.com/question/1384875311724922940.html 调用百度api获得ip对应的城市https://www.cnb ...
- 前端 初级篇(HTML)
HTML 概述: HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,他是一种制作万维网页面标准语言(标记).相当于定义统一的一套规则,大家都来遵守他,这样就 ...
- Loadrunder之脚本篇——参数化在场景中的运用
Action() { lr_eval_string("{NewParam}"); lr_eval_string("{NewParam}"); return 0; ...
- Sliverlight变换特性
通过变换,可以为Silverlight图形对象提供旋转.缩放.扭曲和移动的特效.如果图形元素需要通过修改坐标系统来改变元素显示,可能需要使用变换功能.一般来说,所有的变换使用矩形数学改变图形元素的坐标 ...
- 【CodeChef】Enormous Input Test
The purpose of this problem is to verify whether the method you are using to read input data is suff ...
- Java zip 压缩 文件夹删除,移动,重命名,复制
FileUtil.java import java.io.*; import java.util.List; import java.util.zip.ZipEntry; import java.ut ...