UVA - 1632 Alibaba 记忆化搜索

问题

一条坐标轴,alibaba可以选择任一点作为出发点,直线上一共有N个点,按照顺序输入,每个点有自己的坐标和时间,超过时间会消失,问经过所有点的最短时间是多少,无法办到的话输出No solution

分析

这题和前面的修理长城比较相似,把时间作为状态对应的值,状态是 dp[i][j][p]代表经过[i,j]所有点后停留在p(p代表左端还是右端)所需要的最小时间
注意: 等于算超时

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int maxn=10002,Inf=0x3f3f3f3f;
int N,place[maxn],t[maxn],dp[maxn][maxn][2];

//用dp[i,j,k]更新dp[i2,j2,p2]
inline void update(int i,int j,int p,int cur,int i2,int j2,int p2,int cur2){
    if(dp[i][j][p]==-1) return;
    int diff=abs(place[cur]-place[cur2]),res=dp[i][j][p]+diff;
    if((res<t[cur2]) && (dp[i2][j2][p2]==-1 || dp[i][j][p]+diff<dp[i2][j2][p2])){
        dp[i2][j2][p2]=res;
    }
}

int main(void){
    while(scanf("%d",&N)==1){        //地点按照递增顺序输入
        for(int i=0;i<N;++i){
            scanf("%d%d",&place[i],&t[i]);
            dp[i][i][0]=dp[i][i][1]=0;
        }
        //开始位置一定是一个点上,不可能是两点之间,两点之间作起始点会让最优解变差
        //dp[i][j][p],已经走过了[i,j],p代表左端i还是右端j,到达现在状态所需要的最短时间
        //dp[i][j][p]=-1代表不可能
        for(int d=1;d<N;++d){
            for(int i=0,j=d;j<N;++i,++j){
                dp[i][j][0]=dp[i][j][1]=-1;
                if(dp[i][j-1][0]!=-1) update(i,j-1,0,i,i,j,1,j);
                if(dp[i][j-1][1]!=-1) update(i,j-1,1,j-1,i,j,1,j);
                if(dp[i+1][j][0]!=-1) update(i+1,j,0,i+1,i,j,0,i);
                if(dp[i+1][j][1]!=-1) update(i+1,j,1,j,i,j,0,i);
            }
        }
        if(dp[0][N-1][0]==-1){
            if(dp[0][N-1][1]==-1) printf("No solution\n");
            else printf("%d\n",dp[0][N-1][1]);
        }else{
            if(dp[0][N-1][1]==-1) printf("%d\n",dp[0][N-1][0]);
            else printf("%d\n",min(dp[0][N-1][0],dp[0][N-1][1]));
        }
    }
    return 0;
}

还有,换种遍历区间的方法就会快很多,从2800ms变为500ms,想不明白为什么,理论上应该是差不多的
for(int i=N-2;i>=0;–i)
for(int j=i+1;j<N;++j)

现在我知道原因了,少了一个变量在计数是一部分,时间少了一倍,另一个是二维数组寻址,三个变量的方法中,[i,j]都相当于变量,每次都在变化,所以比较慢,只用两个变量的二重循环,i相对变化比较少,可以看作是常量,计算只需要寻址j,少了一重寻址,时间少了一倍,大概可以这么解释

//参照别人的优化版本
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int maxn=10002,Inf=0x7f7f7f7f;
int N,place[maxn],t[maxn],dp[maxn][maxn][2];

inline void update(int i,int j,int p,int cur,int i2,int j2,int p2,int cur2){
    if(dp[i][j][p]==-1) return;
    int diff=abs(place[cur]-place[cur2]),res=dp[i][j][p]+diff;
    if((res<t[cur2]) && (dp[i2][j2][p2]==-1 || dp[i][j][p]+diff<dp[i2][j2][p2])){
        dp[i2][j2][p2]=res;
    }
}

int main(void){
    while(scanf("%d",&N)==1){        //地点按照递增顺序输入
        for(int i=0;i<N;++i){
            scanf("%d%d",&place[i],&t[i]);
            dp[i][i][0]=0;
        }
        //开始位置一定是一个点上,不可能是两点之间,两点之间作起始点会让最优解变差
        //dp[i][j][p],已经走过了[i,j],p代表左端i还是右端j,到达现在状态所需要的最短时间
        for(int i=N-2;i>=0;--i){
            for(int j=i+1;j<N;++j){
                dp[i][j][0]=dp[i][j][1]=Inf;
                dp[i][j][1]=min(dp[i][j-1][0]+place[j]-place[i],dp[i][j-1][1]+place[j]-place[j-1]);
                dp[i][j][0]=min(dp[i+1][j][0]+place[i+1]-place[i],dp[i+1][j][1]+place[j]-place[i]);
                if(dp[i][j][1]>=t[j]) dp[i][j][1]=Inf;
                if(dp[i][j][0]>=t[i]) dp[i][j][0]=Inf;
            }
        }
        if(dp[0][N-1][0]>=Inf){
            if(dp[0][N-1][1]>=Inf) printf("No solution\n");
            else printf("%d\n",dp[0][N-1][1]);
        }else{
            if(dp[0][N-1][1]>=Inf) printf("%d\n",dp[0][N-1][0]);
            else printf("%d\n",min(dp[0][N-1][0],dp[0][N-1][1]));
        }
    }
    return 0;
}

发布了50 篇原创文章 · 获赞 0 · 访问量 708

猜你喜欢

转载自blog.csdn.net/zpf1998/article/details/104083750