1. 程式人生 > >【線上筆試題解題報告系列】網易2017校招內推筆試之程式設計題【持續更新】

【線上筆試題解題報告系列】網易2017校招內推筆試之程式設計題【持續更新】

網易今年把內推筆試放在牛客網上辦,然後出了一批程式設計題。

題目在:

一共18個,好多(不同崗位抽3個不同的題的樣子)……

慢慢寫吧,做一題寫一題。

以下題解將假定讀者有下列知識,對下面所列舉的細節不再贅述。

(如果有必要對此進行教學的,請站內信我)

C/C++的基本語法

同餘的基本性質

(如果覺得一些細節還要講的更具體的,也歡迎站內信)

飢餓的小易

完整的思考思路:

懶人請跳過前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次,一直到2^300000次

然後,對每個a檢查一下,乘起來結果%1e9+7是不是0,如果是0,說明100000次之內有解

——問:那最小要執行幾次變換?

答:我們直接貪心,儘量讓a乘8(乘2次8和乘3次4一樣大,當然是乘8越多,變換次數越少)

——問:如果我發現a==2^5或a==2^4的時候滿足要求,但是5和4才不能表示成3的倍數,怎麼辦?

答:別忘了你手上還有4x+3的變換(就是a乘4的變換)

對5這種情況,除以3餘2,那剛好,用一次乘4的變換就行了

對4這種情況,除以3餘1,我們考慮,消去一個乘8的變換,用2個乘4的變換代替並補足。

計算上,直接/3作為結果,如果有餘數,就要結果再加1次

#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;

int main(){
    int n;
    while(~scanf("%d",&n)){
        int times=4;
        int ans=100001;
        for(int i=1;i<=300000;++i){
            int x=((long long)(n)*times+times-1)%mod;
            if(x==0){
                ans=(i+1)/3+((i+1)%3?1:0);
                break;
            }
            times=times*2%mod;
        }
        printf("%d\n",ans>100000?-1:ans);
    }
    return 0;
}

擺脫了神慢的map之後,這份程式碼的速度非常理想,用時不足1ms。

不要二

完整的思考過程:

首先把歐幾里得距離不能等於2這個條件轉化一下。

( (x1-x2) * (x1-x2) + (y1-y2) * (y1-y2) ) 的算術平方根不能等於2

也就是(2點x的差值的平方+2點y的差值的平方)的算數平方根不能等於2

考慮差值的平方可能的取值範圍:0、1、4、9、……

怎麼湊出4呢?

有且僅有1種方案:一個是0,一個是4

轉化一下,用自然語言描述:對任何一個放了蛋糕的點(x,y),不能在其上下左右相差2格的地方((x-2,y),(x+2,y),(x,y-2),(x,y+2))放蛋糕。

這樣一轉化,你連乘法、開平方都不用了,只用加減就能描述了。

但是這個怎麼放仍然是很大的問題!

下面用x表示放蛋糕,o表示不放蛋糕

考慮從小規模解決,比如一行:

對寬度為1,直接放

x

對寬度為2,仍然直接放

xx

對寬度為3,中間一個肯定能放,左右兩邊2選1,那就先選左邊的:

xxo

對寬度為4,第1和第3個位置衝突,第2和第4個位置衝突,仍然貪心放左邊:

xxoo

對寬度為5,第1和第3個位置衝突,第2和第4個位置衝突,第3和第5個位置衝突

放第3個位置太吃虧了,打死都不放,放第1和第5個位置

第2和第4個位置隨便選一個,仍然選靠左的:

xxoox

一直往下延伸,你會發現,下面這個放法一定不吃虧:

xxooxxooxxooxxooxxooxxooxxoo……

(放2個,不放2個,放2個,不放2個,一直延續)

一行的問題解決了,考慮往多行放:

第二行直接照抄第一行,一點事也沒有

但是第三行不能照抄(注意y方向也有位置衝突)

轉而考慮列方向,也能得到第一列、第二列xxooxxooxxoo延續下去不吃虧

那第三行、第三列怎麼辦?

我們優先採納行號、列號小的方案,然後發現第3~4行、第3~4列的2*2區間,他們能放,而且和之前的不衝突!

那就放下去啊!

然後我們發現,第三行和第一行相反的放置,就沒有任何問題了,第四行同理,第三、第四列也是這樣。

這樣不斷延伸,我們得到

xxooxxooxxooxxo

xxooxxooxxooxxo

ooxxooxxooxxoox

ooxxooxxooxxoox

xxooxxooxxooxxo

xxooxxooxxooxxo

ooxxooxxooxxoox

ooxxooxxooxxoox

這樣的圖案

——是不是覺得很像國際象棋棋盤?

這樣,你可以選擇暴力把整個圖案畫出來,然後統計多少格子有放蛋糕,作為答案。

當然也可以選擇,直接數學計算:

算出第1、第3行各自能放多少

之後因為是每4行迴圈,直接算迴圈多少次,差多少就直接補差的行。

這麼做,時間複雜度O(1),用時小於1ms的。

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

int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        int firstrow=(m/4*2)+((m%4)<2?m%4:2);
        int secondrow=(m/4*2)+((m%4)>2?m%4-2:0);
        int ans=(n/4)*(firstrow+secondrow)*2;
        switch(n%4){
            case 3:ans+=secondrow;
            case 2:ans+=firstrow;
            case 1:ans+=firstrow;
        }
        printf("%d\n",ans);
    }
    return 0;
}

數列還原

注意到,最多10個數未知。

如果列舉10個數的所有排列呢?

10!=3628800,才400萬不到

考慮到,一般認為電腦1s能做1~2億次運算

那麼我們暴力列舉所以排列是可行的。

但是對每個排列,如何計算其順序對數呢?

第一反應,直接填進去,n^2暴力計算順序對數,

這個方案有點太離譜了——好慢,要算~5000次,這是受不了的。

我們能不能預先計算好大部分結果,對每個排列只用很小的計算量來解決呢?

考慮拆分順序對總數

順序對總數=已經填進去的數之間的順序對數+沒有填進去的數之間的順序對數+已經填進去和沒有填進去的數之間的順序對數

第一部分:只用算1次就行了,複雜度n^2的都是能輕鬆接受的

第二部分:對算出來的每種排列,直接暴力計算吧,計算量最多隻有10*9/2

第三部分:直接預先算好。預先計算出每個未填的數在每個位置上,能與預先填寫好的陣列成的順序對數。

在實現的時候,對每個未填入的數,掃2遍:

一遍從左到右,用一個變數記錄,到當前位置比這個未填的數小的數的數量。如果掃到空位,那就記錄結果(即,假設這個數填入這個空位)

還有一遍從右到左,來計算比這個未填的數大的數的數量。

這個預處理的計算量很小,10*100*2

最後,列舉每種排列,暴力計算排列內的順序對數量,然後查詢第三部分預處理結果,對一個排列,計算量只有45+10=55,遠小於5000多次。

所以最後用時<1ms。(其實我懷疑資料偏弱了……)

最後,我的實現裡,加了2個額外的判斷

1、k>最大可能順序對數,直接輸出0

2、k<已填入的數之間的順序對數,直接輸出0

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

int a[105];
bool appear[105];
int missidx[15];
int missnum[15];
int misscnt=0;
int smaller[105][105];
int larger[105][105];

int calc_orderedPairs(int *num,int n){
    int ans=0;
    for(int i=1;i<n;i++){
        if(num[i])
            for(int j=0;j<i;j++){
                if(num[j]&&num[j]<num[i]){
                    ++ans;
                }
            }
    }
    return ans;
}

int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    if(k>n*(n-1)/2){
        puts("0");
        return 0;
    }
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        if(!a[i])missidx[misscnt++]=i;
        else appear[a[i]]=1;
    }
    misscnt=0;
    for(int i=1;i<=n;i++){
        if(!appear[i]){
            missnum[misscnt++]=i;
        }
    }
    
    //given inner
    int given=calc_orderedPairs(a,n);
    if(given>k){
        puts("0");
        return 0;
    }
    
    //given and not given
    for(int i=0;i<misscnt;++i){
        int small=0,large=0;
        for(int j=0;j<n;j++){
            if(!a[j]){
                smaller[j][missnum[i]]=small;
            }
            else if(a[j]<missnum[i])++small;
        }
        for(int j=n-1;j>=0;--j){
            if(!a[j]){
                larger[j][missnum[i]]=large;
            }
            else if(a[j]>missnum[i])++large;
        }
    }
    
    int ans=0;
    //not given
    do{
        int inner=calc_orderedPairs(missnum,misscnt);
        for(int i=0;i<misscnt;++i){
            inner+=smaller[missidx[i]][missnum[i]];
            inner+=larger[missidx[i]][missnum[i]];
        }
        if(inner+given==k) ++ans;
    }while(next_permutation(missnum,missnum+misscnt));
    
    printf("%d\n",ans);
    return 0;
}

合唱團

……想了半天,居然想不出來如何解釋狀態轉移方程是怎麼想出來的。這隻能說,是套路。

這個思考過程待填,下面直接說狀態的定義。

f [ i ] [ j ] [ 最大 / 最小 ]

分別表示,以第i個人為最後一個(也是必選的)人,加上這個人,已經選了 j 個人,最大可能的乘積和最小可能的乘積。

——為什麼不是隻記錄最大的,還要記錄最小的?

——因為最小的,很可能是一個負數,有著極大的絕對值,再乘一個負數,就變成最大的正數,也就是最優解了。

然後考慮,這個狀態由哪些狀態轉移過來?

j 人,明顯是從j-1個人的狀態,最後加1個人(當前考慮的 i )而來。

第 i 人,根據題目要求,編號差不能大於d。那我們就往前觀察最多d個人,從i-d到i-1,選了j-1個人中,選擇和自己相乘,最大/最小的。

注意考慮邊界條件:只選了一個人,就是 i 自己。

最後,解很大,請使用long long(C++)/ long (Java、C#)來儲存中間計算結果。

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

int a[55];
ll f[55][15][2];

int main(){
    int n,kk,d;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
    }
    scanf("%d%d",&kk,&d);
    ll ans=0;
    for(int i=1;i<=n;i++){
        f[i][1][0]=f[i][1][1]=a[i];
        for(int j=2;j<=kk;++j){
            for(int k=i-1;k>=max(i-d,1);--k){
                f[i][j][0]=max(f[i][j][0],max(f[k][j-1][0]*a[i],f[k][j-1][1]*a[i]));
                f[i][j][1]=min(f[i][j][1],min(f[k][j-1][0]*a[i],f[k][j-1][1]*a[i]));
            }
        }
        ans=max(ans,max(f[i][kk][0],f[i][kk][1]));
    }
    printf("%lld\n",ans);
    return 0;
}

數字遊戲

一開始沒仔細看,稍微想了一下——

網易你好無聊,抄Google出過的題……

看上去google的還強不少!

當時毫不動腦把小資料過掉了,之後過了40多分鐘,想出來大資料的正解……

我們直接說弱化版(網易的版本)應該如何思考吧……

既然選擇結果與輸入順序無關,那就閉著眼睛先排序……

首先我們必須要1個1,不然連1都湊不出來

有1以後,看看要湊2怎麼辦?

1、再來個1,1+1=2

2、來個2,直接得到2

如果更大的數呢?2就湊不出來了,不行。

然後考慮湊3:

如果開始是2個1,那我需要來一個1,1+1+1,或者一個2,1+2,或者一個3,直接成3,但是下一個數是4可不行——湊不出來了

如果開始是一個1,一個2,1+2直接得到3

不停往下考慮,你會發現:

如果我現在前面幾個數能湊出1~x,對下一個數a

——如果a>x+1,那麼x+1就是最小的湊不出來的數

——否則,這個數a能讓湊出來的範圍變成1~x+a

        ——這個很顯然的,只要a+1、a+2、a+3,一直到a+x,就行了。

所以,解法很直接:

排序,然後從最小的到最大,一個個看能否接續下去,擴大從0開始能連續表達的整數範圍,如果能,繼續往下,否則我們就找到了最小的,不能被表達的整數。

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

int a[1005];

int main(){
    int miss=0;
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    sort(a,a+n);
    for(int i=0;i<n;i++){
        if(a[i]>miss+1) break;
        miss+=a[i];
    }
    printf("%d\n",miss+1);
    return 0;
}
——順帶一提,練習裡好像為了大家都能過,把資料範圍削弱到,最多20個數,考試時是100個……

20個的情況下,暴力列舉即可。但是你寫暴力列舉,寫出來比正解長、跑的比正解慢……

至於上面提到的,google的原始版本,推薦大家自行思考一下,思路類似。

(我的題解沒看懂的,去看google版本的吧)

地牢逃脫

這個題面寫的真差!花了一些時間,去揣摩題意,才猜到大概。

“地牢的出口可能在任意某個可以通行的位置上”
還有,“最壞情況下,他需要多少步才可以離開這個地牢”
——這兩句話在一起,導致題意很不明確

第一反應:我去,這傢伙要猜終點?猜終點的話,所有可行方格踏完才知道終點在哪裡都有可能啊!

然後看樣例輸出,說是3步就行,放在右下角就行,走過去步數是最大的。

猜一發,是要我們找一個點放出口,使得牛牛走出去的最優方案的步數是最大的?

如果是這個題意的話,等價於:

求距離牛牛起始位置最遠的點,走到那裡所需要的步數。

這個是很傳統的廣度優先搜尋(下簡稱BFS)的使用。

具體到這個題上,起點最短距離為0,其他點設定成無窮大,然後起點放進佇列。

之後,佇列非空的時候,取出佇列第一個元素,對這個點,嘗試向所有資料裡給出的步伐方向走一走,能走的走過去,看看能否更新最短距離,能就更新,並把這個目的點放入佇列。

最後遍歷距離陣列,有走不到的空點就輸出-1,否則輸出最遠的點的距離。

沒寫過的,或者想確認細節的,直接看程式碼吧。

(還看不懂的,天貓上買一本《資訊學奧賽一本通》,看到第二部分 基礎演算法的第八章 廣度優先搜尋,自己去學習一下。)

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

int n,m;
int direction[55][2];
int dcnt;
char ground[55][55];
int dis[55][55];

struct Point{
    int x,y;
    Point(){}
    Point(int _x,int _y):x(_x),y(_y){}
    Point go(int idx){
        return Point(x+direction[idx][0],y+direction[idx][1]);
    }
    bool isOK(){
        return x>=0&&y>=0&&x<n&&y<m&&ground[x][y]=='.';
    }
};

int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%s",ground[i]);
    }
    Point start;
    scanf("%d%d",&start.x,&start.y);
    scanf("%d",&dcnt);
    for(int i=0;i<dcnt;i++){
        scanf("%d%d",&direction[i][0],&direction[i][1]);
    }
    fill(dis[0],dis[54]+55,INT_MAX);
    dis[start.x][start.y]=0;
    
    queue<Point> q;
    q.push(start);
    while(!q.empty()){
        Point x=q.front();q.pop();
        for(int i=0;i<dcnt;++i){
            Point y=x.go(i);
            if(y.isOK()){
                if(dis[y.x][y.y]>dis[x.x][x.y]+1){
                    dis[y.x][y.y]=dis[x.x][x.y]+1;
                    q.push(y);
                }
            }
        }
    }
    int answer=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(ground[i][j]=='.') answer=max(answer,dis[i][j]);
        }
    }
    printf("%d\n",answer==INT_MAX?-1:answer);
    return 0;
}


====================================群眾喜聞樂見的大水題在下面======================================

分蘋果

現在要求均分這些蘋果,那就先判斷總和能否均分,不能均分當然不行。

之後,還有每次只能一口氣移動2個蘋果

假設就2人,一個人拿5個蘋果,一個人拿11個蘋果。

現在要他們每個人手拿8個蘋果,在每次移動就要移動2個蘋果的條件下,這怎麼可能啊?最多做到一個人7個,一個人9個

——注意到,每次加減2個蘋果,手上的蘋果數量的奇偶性不變

那麼,每個人一開始手上蘋果數量應該和平均值的奇偶性相同,這樣才有可能達到目標狀態,否則絕對不可能達到。

最後,如果上面2個條件都滿足,要移動幾次?

只要看蘋果多了的人要給出幾次就行了,少了的人會有相應的空位的。

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

int a[105];

int main(){
    int n,sum=0,avg=0,ans=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        sum+=a[i];
    }
    if(sum%n){
        puts("-1");
        return 0;
    }
    avg=sum/n;
    for(int i=0;i<n;i++){
        if(a[i]%2!=avg%2){
            puts("-1");
            return 0;
        }
        if(a[i]>avg){
            ans+=(a[i]-avg)/2;
        }
    }
    printf("%d\n",ans);
    return 0;
}


星際穿越

……我不想寫二分怎麼辦?

答:直接對h開根號,開根號的結果x一定會導致x*x+x>h

那我們往下一點點減小x,減到x*x+x<=h為止。

——其實減的次數很少

因為x^2+x-((x-1)^2+(x-1))=2x

減小了2x,你還沒有一個x的空間?我不信。

(為了避免糾結windows和linux上的輸出long long的方式的不一致,直接手寫輸入輸出long long的函式)

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

template <class T>
inline void scan(T &ret) {
    char c; ret=0;
    while((c=getchar())<'0'||c>'9');
    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
}

inline void out(ll x) {
   if(x>9) out(x/10);
   putchar(x%10+'0');
}

int main(){
    ll h;
    scan(h);
    ll x=sqrt(h);
    while(x*x+x>h)--x;
    out(x);
    return 0;
}


藏寶圖

對第一個字串遍歷過去,第二個字串要一個指標p,表示匹配了幾個字元

如果字元相同,p往後移動

最後第一個字串處理完的時候,看p有沒有移動到末尾。

——如果不理解為什麼這麼做的,

回顧一下編譯原理裡的狀態機。

第二個字串裡每個字元當做一個狀態點,相同就後移,不相同就回到自己。

整個狀態機最後追加一個終結節點。

如果停在終結節點,表示文字串被接納,否則不符合,丟棄。

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

char stra[100005];
char strb[100005];

int main(){
    gets(stra);
    gets(strb);
    int p=0;
    for(int i=0;stra[i] && strb[p];++i){
        if(stra[i]==strb[p])++p;
    }
    puts(strb[p]?"No":"Yes");
    return 0;
}

下廚房

把所有字串存下來,想個辦法去重,求剩下來的數量,就好了嘛~

去重辦法非常多,

1、排序後掃一遍,和前面的不同就數量+1

2、使用hashset/hashmap/treeset/treemap等,插入完以後計數

3、其他自帶函式,比如,騷氣的,C# Linq提供的Distinct()走你!

下面給出方案2和3的做法:

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

char str[100005];

int main(){
    set<string> s;
    while(~scanf("%s",str)){
        s.insert(str);
    }
    printf("%d\n",s.size());
    return 0;
}

using System;
using System.Linq;
using System.Collections.Generic;
class Program{
    public static void Main(string[] args){
        List<string> s=new List<string>();
        string str;
        while((str=Console.ReadLine())!=null){
            string[] token=str.Split(' ');
            s.AddRange(token);
        }
        Console.WriteLine(s.Distinct().Count());
    }
}

解救小易

走到(xi,yi)的最小步數明顯是(xi-1)+(yi-1)

只要知道小易到哪個陷阱的最小步數是最小的,就ok了。

——你看大網易多好,座標範圍居然只有1000*1000

出個20億*20億算絕贊好嗎?

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

int x[1005];
int y[1005];

int main(){
    int n,ans=INT_MAX;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&x[i]);
    }
    for(int i=0;i<n;i++){
        scanf("%d",&y[i]);
    }
    for(int i=0;i<n;i++){
        ans=min(ans,x[i]-1+y[i]-1);
    }
    printf("%d\n",ans);
    return 0;
}


相關推薦

線上試題解題報告系列2017筆試程式設計持續更新

網易今年把內推筆試放在牛客網上辦,然後出了一批程式設計題。 題目在: 一共18個,好多(不同崗位抽3個不同的題的樣子)…… 慢慢寫吧,做一題寫一題。 以下題解將假定讀者有下列知識,對下面所列舉的細節不再贅述。 (如果有必要對此進行教學的,請站內信我) C/C++的基本

試題2018秋筆試

今天下午3點到五點做了網易秋招內推筆試題,晚上的時候牛妹就把程式設計題的答案發出來了,聽說有幾萬人參加了,嚇死。。。。。感覺自己太弱。 下面是我做的三道筆試題,一共有八道。。。。。 題目一 問題描述 小易有一個長度為n的整數序列,a_1,…,a_

試題拼多多2018程式設計

2、大資料相乘 問題描述 有兩個用字串表示的非常大的大整數,算出他們的乘積,也是用字串表示。不能用系統自帶的大整數型別。 輸入描述:  空格分隔的兩個字串,代表輸入的兩個大整數 輸出描述:  輸入的乘積,用字串表 問題分析 只要把加法換成乘法就好,半夜

筆試總結2019編程-玩你媽的橡皮泥

nbsp 三種 順序 bubuko 環形隊列 什麽 復雜度 問題 ++ 題目我就不多說什麽了,橡皮泥,一串橡皮泥哦。 非黑即白。 小明可以采取以下方法0或多次: 從某一處切割,讓切割處左右隊列內兩條橡皮泥整個發生反轉,再拼接到一起。 輸入: 橡皮泥隊列 輸出: 最長的黑白相

2017編程——回文序列 解題報告

out += stream pan 唯一性 [1] bsp names length Problem:https://www.nowcoder.com/question/next?pid=2811407&qid=46573&tid=6015849 如果一個數

名企面試經驗-遊戲-提前批SDK遊戲服務端研發工程師

目前正在流程中: 一面: 1、你的技術棧 2、java函式是引用傳遞還是值傳遞 3、程序執行緒的區別是什麼 4、說一下堆的邏輯分割槽 5、說一下你知道的垃圾回收器 6、說一下單執行緒多執行

2019程式設計試題,一條街上n個房子,k個住戶

尚有不足,請高手勿噴,有高見請不吝賜教 import java.util.Scanner; public class T3 { /*一條街上n個房子,k個住戶  * 你需要找一個房子兩邊都有鄰居  * 要求得到所有可能性中,  * 最小符合的房子數和最大符合的房

2019程式設計試題,給定一個N*M矩陣,放了牌,朝上

尚有不足,請高手勿噴,有高見者請不吝賜教 import java.util.Scanner; public class T2 { /*  * 給定一個N*M矩陣,放了牌,朝上  * 對於每個牌進行以下操作  * 翻轉一張牌,與之相鄰的八張牌也翻轉  *

2019程式設計試題,你有三個任務,完成任務要代價。

import java.util.Scanner; /* 你有三個任務,可以不用代價完成一個任務 然後完成第i個任務之後,可以花費|Ai-Aj|的代價 完成第j個任務,||代表絕對值    輸入  三個整數A1 A2 A3    輸出  

2019試題--俄羅斯方塊得分

題目描述: 自定義俄羅斯方塊列數,每次俄羅斯方塊下落個數為1*1,當一行都落滿俄羅斯方塊時,得分+1。現在小明玩到m個俄羅斯方塊,求此時的分數。 輸入: 第一行: 列數, 俄羅斯方塊個數m 第二行 :a(1) a(2) a(3) …a(i)…a(m) 表

2017試題

本人筆試的計算機視覺方向,程式設計題和其他研發崗位類似。 歡迎小夥伴們一起討論出正確答案。 共20個選擇題,3個程式設計題,1個簡答題 一.選擇題 1.Linux中,提供TCP/IP包過濾功能的軟體叫什麼? A.iptables B.r

2018Java開發工程師試卷 String.substring(2,5)的輸出結果為

網易2018校招Java開發工程師筆試卷 題目大意: 字串“ABCDEFG”,使用String.substring(2,5)正確輸出結果是: A. BCD B. BCDE

筆試實踐2---2017線上筆試程式設計

選取是3個題目中的第3題,題目是這樣的: 如果一個數字序列逆置之後跟原序列是一樣的就稱這樣的數字序列為迴文序列。例如: {1, 2, 1}, {15, 78, 78, 15} , {112} 是迴文序

試題2017.9.10愛奇藝校程式設計平方串解題思路

問題描述 平方串: 形如string s = T + T的字串 目標: 對於字串s,除去若干字元使其成為平方串,求該平方串的最大長度。 思路與實現 將s分成兩半,求兩個子串的最大子序列長度(longest common subsequence)

生成指定個數的隨機碼,同時包含大小寫字母和數字H3C試題

【華三通訊2011年校招程式設計題】題目:編寫一個函式,用於生成隨機密碼,引數為隨機密碼長度,密碼必須同時包含大小寫字母和數字。 【思路】先隨機生成大寫字母、小寫字母和數字的個數,然後密碼的每一位都

前端試題陣列去重

陣列去重 題目來自於自己真實筆試,現在總結到博文,算是給自己的再一次複習吧,另外也可以與大家分享。 最初的實現 我記得我第一次的答案是這樣寫的: for (var i = 0; i < arr1.length; i++) { f

Java試題輸出字串的所有組合

1、題目 輸入一個字串,輸出該字串中字元的所有組合。舉個例子,如果輸入abc,它的組合有a、b、c、ab、ac、bc、abc。 2、解題思想 首先建立combine函式,對於字串進行處理,如果字串為空,則直接輸出;如果字串不為空,則建立StingBuild

Java試題輸出字串中第一個不重複的字元

1、題目 在一個字串中找到第一個只出現一次的字元。例如,輸入“abaccdeff”,則輸出b。 2、Java程式碼 public class RetStr { public static

Java試題定義棧的資料結構

1、題目 定義棧的資料結構,請在該型別中實現一個能夠得到棧最小元素的min函式,在該棧中,呼叫min、push和pop的時間複雜度都是O(1)。 2、Java程式碼 public class

Java試題判斷迴文數字

1、題目 有這樣一類數字,它們順著看和倒著看是相同的數,例如:121、656、2332等,這樣的數字就稱為迴文數字。編寫一個Java程式,判斷從鍵盤接收的數字是否為迴文數字。 2、解題思想 從迴文數字的特點出發,弄清楚其特點是解決本問題的關鍵。解決方案可以