【在线笔试题解题报告系列】网易2017校招内推笔试之编程题【持续更新】

网易今年把内推笔试放在牛客网上办,然后出了一批编程题。

题目在:

http://www.nowcoder.com/test/2252286/summary

http://www.nowcoder.com/test/2252291/summary

一共18个,好多(不同岗位抽3个不同的题的样子)……

慢慢写吧,做一题写一题。


以下题解将假定读者有下列知识,对下面所列举的细节不再赘述。

(如果有必要对此进行教学的,请站内信我)

C/C++的基本语法

同余的基本性质


(如果觉得一些细节还要讲的更具体的,也欢迎站内信)


本文地址:http://blog.csdn.net/fcxxzux/article/details/52138964


饥饿的小易

网址:http://www.nowcoder.com/questionTerminal/5ee8df898312465a95553d82ad8898c3

完整的思考思路:


懒人请跳过前2份代码和讲解,谢谢!


考虑正向枚举,直觉上,直接放弃——对无解的情况,你得每一步都考虑展开,每一步展开就2个分支,10万步最多2^100000个分支,怎么可能算完……

(我们回头看看这个做法)

那就考虑逆向枚举。


但是还有一个问题:位置的下标还是指数级增长的(x->x*4+3或x*8+7)

注意到,贝壳总生长在能被1,000,000,007(之后写作1e9+7)整除的位置。

利用这一点,考虑同余的性质,把下标用%1e9+7后的结果表示(因为我根本不在乎,最后具体数值,我只在意,下标是不是1e9+7的倍数)。


然后来倒着做:

从0(下标是1e9+7的情况)开始倒着推算,正着计算是先乘再加,倒着就要先减再除。

——减成了负数怎么办?

——加上1e9+7,变成等价正数(或者说,计算出其加法逆元)

——不能整除怎么办?就说明不可往后推吗?

——我上来就踩了这个坑……

除法并没有同余的性质,但是我们还有乘法逆元。

/4,在%1e9+7的意义下,等价于乘(1e9+8)/4=(2.5e8+2)

/8,在%1e9+7的意义下,等价于乘(1e9+8)/8=(1.25e8+1)

——然后我们愉快的用乘法替代除法吧!


ok,这样倒推最多100000步,记录每个数有没有被访问过,访问过就不要重复展开(像BFS一样),得到一个表,记录了每个数变为0要多少步

——试验运行一下,发现好像这部分的计算飞快啊!

之后管他来什么数,直接查表,表里没有,就是100000步达不到的,否则100000步可达,输出结果。

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
#include <map> 
#include <queue>
using namespace std;
typedef long long ll;

const int mod=1e9+7;
map<int,int> dis;

int main(){
    dis[0]=0;
    queue<int> q;
    q.push(0);
    while(!q.empty()){
        int x=q.front();q.pop();
        int y1=(x-7+mod)%mod;
        y1=((long long)(125000001LL)*y1)%mod;
        if(!dis[y1]){
            dis[y1]=dis[x]+1;
            if(dis[y1]<100000){
                q.push(y1);
            }
        }
        int y2=(x-3+mod)%mod;
        y2=((long long)(250000002LL)*y2)%mod;
        if(!dis[y2]){
            dis[y2]=dis[x]+1;
            if(dis[y2]<100000){
                q.push(y2);
            }
        }
    }
    int n;
    while(~scanf("%d",&n)){
        printf("%d\n",dis[n]?dis[n]:-1);
    }
    return 0;
}

这样能通过了。

但是回头思考一下,不对啊!

这么做,最坏情况下也得展开2^100000个点啊,就算倒推角度,明确缩小了范围到1e9+7以内,但是1e9+7仍然很多!

那么正着做呢?

于是勇敢的写一发,直接提交,也通过了!

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
#include <map> 
#include <queue>
using namespace std;
typedef long long ll;

const int mod=1e9+7;
map<int,int> dis;

int main(){
    int n;
    while(~scanf("%d",&n)){
        dis[n]=0;
        queue<int> q;
        q.push(n);
        while(!q.empty()){
            int x=q.front();q.pop();
            int y1=(x*8LL+7)%mod;
            if(!dis[y1]){
                dis[y1]=dis[x]+1;
                if(dis[y1]<100000){
                    q.push(y1);
                }
            }
            int y2=(x*4LL+3)%mod;
            if(!dis[y2]){
                dis[y2]=dis[x]+1;
                if(dis[y2]<100000){
                    q.push(y2);
                }
            }
        }
        printf("%d\n",dis[0]?dis[0]:-1);
    }
    return 0;
}

但是上面2种做法,都很慢,用时显示都有300ms以上了。


继续思考:

既然正着做,去重,不重复展开就能过,那就说明重复非常非常多(你可以试试看在倒着做的代码里,输出有多少能达到的,只有30万个),但是为什么有那么多?

观察变换形式,并做变形:

4x+3=4(x+1)-1

8x+7=8(x+1)-1

如果多层嵌套呢?

y=4x+3

8y+7=8((4(x+1)-1)+1)-1=8(4(x+1))-1=32(x+1)-1

如果你多枚举一些,就会发现,能变换出的数的形式都是:

a(x+1)-1,其中a是2的>=2的幂次数(4、8、16、32、64、……)

我们能否利用这个特点呢?


当然能!

考虑直接枚举那个a,从2^2一直到……等等,最大是2的多少次?

答:直接考虑最大情况,每次变换都选择8x+7那种,也就是,每次a乘上8,也就是说,最坏是(2^3)^100000=2^300000次

所以,枚举a,从2^2次࿰

  • 10
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值