开车旅行

Posted karryw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了开车旅行相关的知识,希望对你有一定的参考价值。

题目链接

真真一道倍增好题。

先说70分思路

我的:
从后边的城市往前跑,这样就能\(n^2\)时间内得到从城市\(i\)到城市\(j\)的路程了。........反正超级麻烦,最后也没写出来。后来想想最直接的暴力好像是\(n*m\),70分也能过。

代码估计以后也看不懂了。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int N = 1005;
const int inf = 2147483647;
map<int,map<int,int> >mp;
int n,h[N],m,s[N],x[N],x0,dis[1005][1005],dist[N],dis1[N][2];
int head[N],tot,maxx[N][21],min1,min2,id1[N],id2[N];
int head1[N],tot1,disa[1005][1005][2],disb[1005][1005][2],ansxo;
struct edge
    int node,next,data;
e[N<<1],e1[N<<1];
struct node
    int tmp[N];
a[N];
void add(int x,int y)

    e[++tot].node=y; e[tot].next=head[x];
    head[x]=tot; 

void add1(int x,int y)

    e1[++tot1].node=y; e1[tot1].next=head1[x];
    head1[x]=tot1; 

inline int read()

    char c=getchar();
    int ans=0,w=1;
    while((c<'0'||c>'9')&&c!='-') c=getchar();
    if(c=='-')  w=-1;c=getchar(); 
    while(c>='0'&&c<='9')
     ans=ans*10+c-'0'; c=getchar(); 
    return ans*w; 

void dfs(int t,int u,int ft,int flag,int lena,int lenb)

//  if(u==1) return ;
    if(flag) 
    
        for(int i=head[u];i;i=e[i].next)
        
            int v=e[i].node;
            disa[v][t][ft]=lena+e[i].data;
            disb[v][t][ft]=lenb;
            a[v].tmp[t]=disa[v][t][ft]+disb[v][t][ft];
            mp[v][a[v].tmp[t]]=t;
            dfs(t,v,ft,0,lena+e[i].data,lenb);
        
    
    else 
    
        for(int i=head1[u];i;i=e1[i].next)
        
            int v=e1[i].node;
            dfs(t,v,ft,1,lena,lenb+e1[i].data);
        
    
 
double chu(int a,int b)

    if(b==0) return 2e9;
    return a/b;

int main()

//  memset(min1,0x3f,sizeof(min1));
//  memset(min2,0x3f,sizeof(min2));
    n=read();
    for(int i=1;i<=n;i++) h[i]=read();
    x0=read(); m=read();
    for(int i=1;i<=m;i++)
     s[i]=read(),x[i]=read();
    for(int i=1;i<=n;i++)
    
        min1=min2=inf;
//      id1=n+1,id2=n+1;
     for(int j=i+1;j<=n;j++)
     
        dis[i][j]=abs(h[i]-h[j]);
        if(dis[i][j]==min1&&)
        
            if(h[j]<h[id1[i]])
            
                min2=min1; id2[i]=id1[i];
                min1=dis[i][j]; id1[i]=j;
            
            else min2=dis[i][j],id2[i]=j;
            continue;
        
        if(dis[i][j]==min2&&h[j]<h[id2[i]])
        
            min2=dis[i][j]; id2[i]=j;
            continue;
        
        if(dis[i][j]<min1)
         min2=min1,min1=dis[i][j],id2[i]=id1[i],id1[i]=j;
        else if(dis[i][j]<min2)
         min2=dis[i][j],id2[i]=j;
     
    
    for(int i=1;i<=n;i++)
    add1(id1[i],i),add(id2[i],i);
    for(int i=1;i<=n;i++)
    
        dfs(i,i,0,0,0,0); dfs(i,i,1,1,0,0);
    
    double ansx=2e9;
    for(int i=1;i<=n;i++)
    
        sort(a[i].tmp+1,a[i].tmp+n+1);
        int l=1,r=n,ansp;
        while(l<=r)
        
            int mid=(l+r)>>1;
            if(a[i].tmp[mid]<=x0) l=mid+1,ansp=mid;
            else r=mid-1;
        
        int tt=mp[i][a[i].tmp[ansp]];
        if(chu(disa[i][tt],disb[i][tt])<ansx) ansx=(disa[i][tt]/disb[i][tt]),ansxo=i;
        if(chu(disa[i][n],disb[i][n])<ansx) ansx=(disa[i][n]/disb[i][n]),ansxo=i;
    
 

\(llfz\)的:
最最暴力的解法。以每个点为起点,进行\(dfs\)。但是这么简单的思路在调试时还是出现了很多错误。导致第一次提交只有15分。
错点:
初始化漏掉情况(\(min1\)表示最近\(min2\)表示次近,\(id1\)\(id2\)分别记录最近和次近的编号,\(dis[i][j]\)暂且表示城市\(i\)到城市\(j\)的距离)

1.有相等的情况

\(min1\)相等
分海拔与\(id1\)比较的两种情况
如果海拔比\(id1\)高,还要与\(id2\)比较海拔,不能直接赋值。万一\(min1==min2\)\(id2\)的海拔更低呢。

2.没有相等的情况

这个比较简单,先比较\(min1\)再比较\(min2\)就行了。

更新答案时漏掉情况。
如果小B走的距离是0,也有可能更新答案。注意,这次是选海拔高的城市。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N = 1005;
const int inf = 2e9;
int n,h[N],x0,m,s[10*N],x[10*N],st;
int min1=inf,min2=inf,id1[N],id2[N];
double ans=2e9;
bool book;
void dfs1(int u,int flag,int s1,int s2,int xp)

    if(book) return ;
    if(u==n)
    
        book=1;
        printf("%d %d\n",s1,s2);
        return ;
    
    if(u==n-1&&flag)
    
        book=1;
        printf("%d %d\n",s1,s2);
        return ;
    
    if(flag)
    
        if(abs(h[u]-h[id2[u]])+s1+s2>xp)
        
            book=1;
            printf("%d %d\n",s1,s2);
            return ;
        
        dfs1(id2[u],0,s1+abs(h[u]-h[id2[u]]),s2,xp);
    
    else
    
        if(abs(h[u]-h[id1[u]])+s1+s2>xp)
        
            book=1;
            printf("%d %d\n",s1,s2);
            return ;
        
        dfs1(id1[u],1,s1,s2+abs(h[u]-h[id1[u]]),xp);
    

double dfs(int begin,int u,int flag,int s1,int s2,int xp)

    if(u==n)
    
        if(s2==0) return 2e9;
        else return (double)s1/s2;
/*      if(s2==0) return ;
//      if(s2==0&&h[begin]>h[st]&&ans==(double)2e9)  st=begin; return ;
        if((double)(s1/s2)==ans&&h[begin]>h[st])
          st=begin;
        if((double)(s1/s2)<ans)
         ans=(double)(s1/s2),st=begin;
        return ;*/
    
    if(u==n-1&&flag)
    
        if(s2==0) return 2e9;
        else return (double)s1/s2;
/*      if(s2==0) return ;
//      if(s2==0&&h[begin]>h[st]&&ans==(double)2e9)  st=begin; return ;
        if((double)(s1/s2)==ans&&h[begin]>h[st])
          st=begin;
        if((double)(s1/s2)<ans)
         ans=(double)(s1/s2),st=begin;
        return ;*/
    
    if(flag)
    
        if(abs(h[u]-h[id2[u]])+s1+s2>xp)
        
        if(s2==0) return 2e9;
        else return (double)s1/s2;
/*          if(s2==0) return ;
//          if(s2==0&&h[begin]>h[st]&&ans==(double)2e9)  st=begin; return ;
            if((double)(s1/s2)==ans&&h[begin]>h[st])
             st=begin;
            if((double)(s1/s2)<ans)
             ans=(double)(s1/s2),st=begin;
            return ;*/
        
        dfs(begin,id2[u],0,s1+abs(h[u]-h[id2[u]]),s2,xp);
    
    else
    
        if(abs(h[u]-h[id1[u]])+s1+s2>xp)
        
            if(s2==0) return 2e9;
            else return (double)s1/s2;
/*          if(s2==0) return ;
//          if(s2==0&&h[begin]>h[st]&&ans==(double)2e9)  st=begin; return ;
            if((double)(s1/s2)==ans&&h[begin]>h[st])
             st=begin;
            if((double)(s1/s2)<ans)
             ans=(double)(s1/s2),st=begin;
            return ;*/
        
        dfs(begin,id1[u],1,s1,s2+abs(h[u]-h[id1[u]]),xp);
    

int main()

//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
     scanf("%d",&h[i]);
    h[0]=2e9;
    scanf("%d%d",&x0,&m);
    for(int i=1;i<=m;i++)
     scanf("%d%d",&s[i],&x[i]);
    for(int i=1;i<=n;i++)
     min1=min2=inf;
     for(int j=i+1;j<=n;j++)
     
        if(min1==abs(h[i]-h[j]))
        
            if(h[j]<h[id1[i]])
            
                min2=min1; id2[i]=id1[i];
                min1=abs(h[i]-h[j]); id1[i]=j;
             
            else 
            
                if(min2==abs(h[i]-h[j])&&h[j]<h[id2[i]])
                 min2=abs(h[i]-h[j]),id2[i]=j;
                if(min2>abs(h[i]-h[j]))
                 min2=abs(h[i]-h[j]),id2[i]=j;
            
            continue;
         
        if(min2==abs(h[i]-h[j]))
        
            if(h[j]<h[id2[i]])
             min2=abs(h[i]-h[j]),id2[i]=j;
            continue;
        
        if(min1>abs(h[i]-h[j])) 
        
            min2=min1; id2[i]=id1[i];
            min1=abs(h[i]-h[j]); id1[i]=j;
        
        else if(min2>abs(h[i]-h[j]))
         min2=abs(h[i]-h[j]),id2[i]=j;
     
    
    for(int i=1;i<=n;i++)
    
        double t=dfs(i,i,1,0,0,x0);
        if(t<ans)  ans=t; st=i;
        if(t==ans&&h[i]>h[st])  ans=t,st=i;
    
    printf("%d\n",st);
    for(int i=1;i<=m;i++)
    
        book=0;
        dfs1(s[i],1,0,0,x[i]);
    
    return 0;

果然还是\(llfz\)强。

正解

对于每个点来说,以它为起点的路径是唯一的。用\(f[i][j]\)表示从第\(i\)点开始向后\(2*2^j\)步到达的点,同样\(sta[i][j]\)\(stb[i][j]\)记录小A和小B走的路径长度。

看的大佬的这篇博客

以后好好复习代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
const int N = 100005;
int n,m,l,r,j;
struct node
    int i,v,l,r;
    bool friend operator < (const node& a,const node& b)
        return a.v<b.v;
    
d[N];
int h[N],p[N],sta[N][21],stb[N][21],f[N][21];
int na[N],nb[N],a,b,ans=n;
double minn = 2e9;
bool zuo()

    if(!l) return 0;
    if(!r) return 1;
    return d[j].v-d[l].v<=d[r].v-d[j].v;

int pd(int a,int b)

    if(!a) return d[b].i;
    if(!b) return d[a].i;
    if(d[j].v-d[a].v<=d[b].v-d[j].v) return d[a].i;
    return d[b].i;

void make_st()

//  int i,j;
    for(int j=1;j<=19;j++)
     for(int i=1;i<=n;i++)
     
        f[i][j]=f[f[i][j-1]][j-1];
        sta[i][j]=sta[i][j-1]+sta[f[i][j-1]][j-1];
        stb[i][j]=stb[i][j-1]+stb[f[i][j-1]][j-1];
     

void getab(LL x,int p)

    a=b=0;
    for(int i=19;i>=0;i--)
    
        if(f[p][i]&&(LL)(a+b+sta[p][i]+stb[p][i])<=x)
        
            a+=sta[p][i]; b+=stb[p][i];
            p=f[p][i];
        
    
    if(na[p]&&a+b+sta[p][0]<=x) a+=sta[p][0];
 
int main()

    LL x;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&d[i].v);
    for(int i=1;i<=n;i++) d[i].i=i;
    sort(d+1,d+n+1);
    for(int i=1;i<=n;i++) p[d[i].i]=i;//p[i]表示编号为i的城市当前在的位置  
    for(int i=1;i<=n;i++) d[i].l=i-1,d[i].r=i+1;
    d[1].l=d[n].r=0;
    for(int i=1;i<=n;i++)
    
        j=p[i]; l=d[j].l; r=d[j].r;
        if(zuo()) nb[i]=d[l].i,na[i]=pd(d[l].l,r);
        else nb[i]=d[r].i,na[i]=pd(l,d[r].r);
        if(l) d[l].r=r; if(r) d[r].l=l;
    
    for(int i=1;i<=n;i++)
    
        f[i][0]=nb[na[i]];//小A先走,走到na[i],然后小B再走,走到nb[na[i]]  
        sta[i][0]=abs(d[p[i]].v-d[p[na[i]]].v);
        stb[i][0]=abs(d[p[f[i][0]]].v-d[p[na[i]]].v);
    
    make_st();
    scanf("%lld%d",&x,&m);
    for(int i=1;i<=n;i++)
    
        getab(x,i);
        if(b&&1.0*a/b<minn)
        
            minn=1.0*a/b;
            ans=i;
        
    
    printf("%d\n",ans);
    for(int i=1;i<=m;i++)
    
        scanf("%d%lld",&j,&x);
        getab(x,j);
        printf("%d %d\n",a,b);
    
    return 0;

全靠代码占长度。。。

以上是关于开车旅行的主要内容,如果未能解决你的问题,请参考以下文章

codevs1199 开车旅行

[NOIP2012] 开车旅行

NOIP2012开车旅行

题解 P1081 开车旅行

NOIP2012开车旅行 倍增

luogu1081 开车旅行 树上倍增