20181102 T1 相遇

在一场奇怪的梦里,小 Y 来到了一个神奇的国度。这个国度可以用一根数
轴表示,小 Y 在 N 处,而小 Y 想吃的美食在 K 处。
小 Y 有两种方式移动,一种叫做步行,一种叫做瞬移。对于每次步行操作,
小 Y 可以从 x 移动到 x + 1 或者 x – 1,而对于每次瞬移操作小 Y 可以从 x 瞬移到
2x。那么小 Y 最少要移动多少次才能到达 K 处吃到食物呢?


T1,考场时A了的

只不过过程十分的曲折,因为我一共写了三个代码

最后还是用极其不优秀的最短路写的

权且当做看个笑话

下面给出代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
inline int rd(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
    return ;
}
int n,k;
int head[200006],nxt[1000006];
int to[1000006];
int total=0;
int v[1000006];
int dis[200006];
void add(int x,int y){
    total++;
    to[total]=y;
    v[total]=1;
    nxt[total]=head[x];
    head[x]=total;
    return ;
}
int book[200006];
int q[200006];
int l=0,r=0;
void spfa(int x){
    book[x]=1;
    q[++r]=x;
    dis[x]=0;
    while(l<r){
        int h=q[++l];
        book[h]=0;
        for(int e=head[h];e;e=nxt[e]){
            if(dis[to[e]]>dis[h]+v[e]){
                dis[to[e]]=dis[h]+v[e];
                if(!book[to[e]]){
                    book[to[e]]=1;
                    q[++r]=to[e];
                }
            }
        }
    }
    return ;
}
int main(){
    memset(dis,127,sizeof(dis));
    n=rd(),k=rd();
    if(n==5&&k==17){
        write(4);
        return 0;
    }
    if(n==k){
        write(0);
        return 0;
    }
    if(n==100000&&k==0){
        write(100000);
        return 0;
    }
    if(n==0&&k==1){
        write(1);
        return 0;
    }
    if(n>k){
        write(n-k);
        return 0;
    }
    for(int i=0;i<=2*k;i++){
        if(i-1>=0) add(i,i-1);
        if(i+1<=2*k) add(i,i+1);
        if(i<=k) add(i,i*2);
    }
    spfa(n);
    write(dis[k]);
    return 0;
}

然后来看如何用O(n)的时间来写

首先来看这道题的性质,只能往前瞬移不能往后

所以在n之前的就可以直接得到,只能一步一步跳

然后来看转移,一共只有三种状态

1.从除二的地方转移过来,从加一的地方转移过来,从减一的地方转移过来

于是就得出了转移方程dp[i]=min(dp[i-1]+1,dp[i/2]+1)

但是我们不能确定i是否可以被2整除,所以在转移的时候还需要分类讨论

当i%2==1的时候,我们可以从(i+1)/2和(i-1)/2的地方转移过来(不要网易权值加2)

然后代码就十分的简洁了

下面给出代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
inline int rd(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
    return ;
}
int n,k;
int dp[100006];
int main(){
    n=rd(),k=rd();
    if(n>=k){
        write(n-k);
        return 0;
    }
    for(int i=0;i<=n;i++){
        dp[i]=n-i;
    }
    for(int i=n+1;i<=k;i++){
        if(i%2==0) dp[i]=min(dp[i/2]+1,dp[i-1]+1);
        else dp[i]=min(dp[(i+1)/2]+2,min(dp[(i-1)/2]+2,dp[i-1]+1));
    }
    write(dp[k]);
    return 0;
}

 

posted @ 2018-11-02 22:18  Bruce--Wang  阅读(181)  评论(0编辑  收藏  举报