• UVA


    题目链接

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

    根据题意显然有状态转移方程$left{egin{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} ight.$

    复杂度是$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=10000+5,inf=0x3f3f3f3f;
     5 int dp[N][N][2],n,x[N],t[N],ans;
     6 
     7 int main() {
     8     while(scanf("%d",&n)==1) {
     9         for(int i=0; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
    10         for(int i=0; i<n; ++i)dp[i][i][0]=dp[i][i][1]=0;
    11         for(int l=n-1; l>=0; --l)
    12             for(int r=l+1; r<n; ++r) {
    13                 dp[l][r][0]=min(dp[l+1][r][0]+x[l+1]-x[l],dp[l+1][r][1]+x[r]-x[l]);
    14                 dp[l][r][1]=min(dp[l][r-1][0]+x[r]-x[l],dp[l][r-1][1]+x[r]-x[r-1]);
    15                 if(dp[l][r][0]>=t[l])dp[l][r][0]=inf;
    16                 if(dp[l][r][1]>=t[r])dp[l][r][1]=inf;
    17             }
    18         int ans=min(dp[0][n-1][0],dp[0][n-1][1]);
    19         if(ans==inf)printf("No solution
    ");
    20         else printf("%d
    ",ans);
    21     }
    22     return 0;
    23 }
    View Code

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

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

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

    和上面的代码唯一的不同之处就是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=10000+5,inf=0x3f3f3f3f;
     5 int dp[2][N][2],vis[2][N][2],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&1][r][p]!=l)vis[l&1][r][p]=l,dp[l&1][r][p]=inf,q.push({l,r,p});
    11     if(dp[l&1][r][p]>ad)dp[l&1][r][p]=ad;
    12 }
    13 
    14 int bfs() {
    15     memset(vis,-1,sizeof vis);
    16     int ret=inf;
    17     while(!q.empty())q.pop();
    18     for(int i=0; i<n; ++i)upd(i,i,0,0);
    19     while(!q.empty()) {
    20         int l=q.front().l,r=q.front().r,p=q.front().p;
    21         q.pop();
    22         if(l==0&&r==n-1) {ret=min(ret,dp[l&1][r][p]); continue;}
    23         int P=p==0?l:r;
    24         if(l>0&&dp[l&1][r][p]+x[P]-x[l-1]<t[l-1])upd(l-1,r,0,dp[l&1][r][p]+x[P]-x[l-1]);
    25         if(r<n-1&&dp[l&1][r][p]+x[r+1]-x[P]<t[r+1])upd(l,r+1,1,dp[l&1][r][p]+x[r+1]-x[P]);
    26     }
    27     return ret;
    28 }
    29 
    30 int main() {
    31     while(scanf("%d",&n)==1) {
    32         ++ka;
    33         for(int i=0; i<n; ++i)scanf("%d%d",&x[i],&t[i]);
    34         int ans=bfs();
    35         if(ans==inf)printf("No solution
    ");
    36         else printf("%d
    ",ans);
    37     }
    38     return 0;
    39 }
    View Code

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

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

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

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

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

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

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

  • 相关阅读:
    Minio对象存储
    白话解说TCP/IP协议三次握手和四次挥手
    企业环境下MySQL5.5调优
    Mac下iTerm2配置lrzsz功能
    七牛云图床和Markdown使用
    SSIS: 把存储在数据库中的图片导出来
    关闭Outlook的时候使之最小化
    【转】CTE(公用表表达式)
    通过SSIS监控远程服务器磁盘空间并发送邮件报警
    在Windows Server 2008 R2 中架设 SMTP 服务器
  • 原文地址:https://www.cnblogs.com/asdfsag/p/10448298.html
Copyright © 2020-2023  润新知