开车旅行
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;
全靠代码占长度。。。
以上是关于开车旅行的主要内容,如果未能解决你的问题,请参考以下文章