写了个dfs,发现无法记忆化。
然后就不知道了。
后来想到其实最后肯定会找两个点来回走。显然来回走希望这段路是最短的,但是如果要走到这个点的话,距离又不能保证很短,所以就不会了。。
题目大意:
给n个点在一维数轴上有不同的位置,现在给定一个初始点(不在n个点上),问访问m个点(可以重复访问)最少的路程。
思路:
首先dfs肯定是可写的,但是复杂度太高了。
应该可以想到比较理想的情况是在两个点之间走来走去。
假设挑了两个点t和t+1,从p走到t的话这段距离并不一定是值得的(即可能走过来花的路程更长)。
因此直接算是不可行的,不过我们采用枚举t和t+1(这里没想到。。)
那么在到达t之前,我们一定是直接走的!
乍一想好像没错,但是有个hack点,例如3个点(1,10,14),需要访问3个点
初始位置p=2,那么我们从p走到10,然后10和14来回走,一共花费8+4+4=16
实际上,我们可以从p走到1,然后1走到10,再走到14,一共花费1+9+4=14
也就是说,我们可能会先往反方向走一个点,然后再朝着目标点t走。
那有没有可能是反方向走若干个点,再朝目标点t走呢?
这里不会的。
我们回顾刚刚反方向走的目的:其实是让反方向走的路t1,取代最后目标点走来走去的一次路程t2,也就是让t1<t2的时候我们可以先反方向走一下。
此时如果我再往反方向走一格的话,这段路假设为t3,我们其实是希望t3<t2。这样的话,我们直接走t3来回就好了,没有必要跑到t去了。
因此,我们的策略就是枚举走来走去的两个点的位置,然后考虑直接走过去或者反方向走一次然后走过去的情况就好了。
这里我把我的dfs改了,也就是在dfs过程中,不需要考虑所有的情况,只要走到某个点时,计算一下当前这个点作为走来走去的点的总路程,最后取最小值就好了。
哎,感觉好难啊。
代码:
#include<bits/stdc++.h> using namespace std; int n,m,pos[100040],p,vis[100004]; long long sum=1e12; void dfs(int k,int t,long long now){ if(k==m+1){ sum=min(sum,now); return ; } if(t>1)sum=min(sum,now+1ll*(m-k+1)*(pos[t]-pos[t-1])); if(t<n)sum=min(sum,now+1ll*(m-k+1)*(pos[t+1]-pos[t])); if(t>1&&!vis[t-1]){ vis[t-1]=1; dfs(k+1,t-1,now+pos[t]-pos[t-1]); } if(t<n&&!vis[t+1]){ vis[t+1]=1; dfs(k+1,t+1,now+pos[t+1]-pos[t]); } return ; } int main() { int T; cin>>T; while(T--){ sum=1e12; cin>>n>>m>>p; int x=0; for(int i=1;i<=n;i++)cin>>pos[i]; pos[n+1]=1e9+7; for(int i=0;i<=n;i++){ if(p>pos[i]&&p<pos[i+1]){ x=i; break; } } memset(vis,0,sizeof(vis)); if(x==0){ //开始只能往右 vis[1]=1; dfs(2,1,pos[1]-p); } else if(x==n){ //开始只能往左 vis[n]=1; dfs(2,n,p-pos[n]); } else { //在中间,尝试往左和往右 vis[x]=1; dfs(2,x,p-pos[x]); memset(vis,0,sizeof(vis)); vis[x+1]=1; dfs(2,x+1,pos[x+1]-p); } cout<<sum<<endl; } return 0; }