網易2017春招筆試真題程式設計題集合題解
前言
想想已經有一年多沒有接觸演算法題了,忙活了一年多沒什麼用的東西,才陡然發現自己竟然就要畢業了,然而審視了下自己的水平估計還達不到大一的程度,甚是驚恐。於是下定決心開始刷一點題,打好基本功。正好有同學在做網易筆試題的時候來向我問問題,我看了看有12道,好像也不多,於是就順便刷了刷。本以為會是一帆風順的,可是事實是,我果然還是太菜了。。。
1、雙核處理
題目
一種雙核CPU的兩個核能夠同時的處理任務,現在有n個已知資料量的任務需要交給CPU處理,假設已知CPU的每個核1秒可以處理1kb,每個核同時只能處理一項任務。n個任務可以按照任意順序放入CPU進行處理,現在需要設計一個方案讓CPU處理完這批任務所需的時間最少,求這個最小的時間。
輸入描述
輸入包括兩行:
第一行為整數n(1 ≤ n ≤ 50)
第二行為n個整數lengthi,表示每個任務的長度為length[i]kb,每個數均為1024的倍數。
輸出描述
輸出一個整數,表示最少需要處理的時間
輸入例子
5
3072 3072 7168 3072 1024
輸出例子
9216
分析
題意很清晰,就是給一個數組,要我們把他分成兩份並分別求和,使得這兩個和的最大值最小。
我下意識的想法是枚舉出所有有可能的和,但是複雜度大概是O(2n)O(2n),貌似會超時。可是仔細一看,length[i]length[i]的取值在[1,4096][1,4096]之間,那麼最多n個數的和的範圍肯定在[n,4096n][n,4096n]之間。那麼我們只要對能取到的和打個表,而且表的長度肯定不超過4096∗50=2048004096∗50=204800,再加上總共遍歷n遍,計算量大概是1e7的級別,可解。
我的答案
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
int n;
int a[51];
set<int>s;
int sum=0;
int main(){
cin>>n;
for(int i=0;i<n;i++){
int tmp;
cin>>tmp;
a[i]=tmp/ 1024;
sum+=a[i];
}
s.insert(0);
for(int i=0;i<n;i++){
set<int>added;
for(set<int>::iterator it=s.begin();it!=s.end();it++){
added.insert(*it+a[i]);
}
s.insert(added.begin(),added.end());
}
int ans=sum;
for(set<int>::iterator it=s.begin();it!=s.end();it++){
ans=min(ans,max(*it,sum-*it));
}
cout<<ans*1024<<endl;
}
2、趕去公司
題目
終於到週末啦!小易走在市區的街道上準備找朋友聚會,突然伺服器發來警報,小易需要立即回公司修復這個緊急bug。假設市區是一個無限大的區域,每條街道假設座標是(X,Y),小易當前在(0,0)街道,辦公室在(gx,gy)街道上。小易周圍有多個計程車打車點,小易趕去辦公室有兩種選擇,一種就是走路去公司,另外一種就是走到一個計程車打車點,然後從打車點的位置坐計程車去公司。每次移動到相鄰的街道(橫向或者縱向)走路將會花費walkTime時間,打車將花費taxiTime時間。小易需要儘快趕到公司去,現在小易想知道他最快需要花費多少時間去公司。
輸入描述:
輸入資料包括五行:
第一行為周圍計程車打車點的個數n(1 ≤ n ≤ 50)
第二行為每個計程車打車點的橫座標tX[i] (-10000 ≤ tX[i] ≤ 10000)
第三行為每個計程車打車點的縱座標tY[i] (-10000 ≤ tY[i] ≤ 10000)
第四行為辦公室座標gx,gy(-10000 ≤ gx,gy ≤ 10000),以空格分隔
第五行為走路時間walkTime(1 ≤ walkTime ≤ 1000)和taxiTime(1 ≤ taxiTime ≤ 1000),以空格分隔
輸出描述:
輸出一個整數表示,小易最快能趕到辦公室的時間
輸入例子:
2
-2 -2
0 -2
-4 -2
15 3
輸出例子:
42
分析
基礎題,先算步行的時間,再列舉每個打車點算出打的時間,找到時間最短的即可。
我的答案
#include<iostream>
#include<algorithm>
#define MAX 100000000 using namespace std;
int n;
int tx[51],ty[51];
int gx,gy;
int wt,dt;
int main(){
cin>>n;
int walkTime=MAX,driveTime=MAX;
for(int i=0;i<n;i++){
cin>>tx[i];
}
for(int i=0;i<n;i++){
cin>>ty[i];
}
cin>>gx>>gy>>wt>>dt;
walkTime=(abs(gx)+abs(gy))*wt;
for(int i=0;i<n;i++){
driveTime=min(driveTime,(abs(ty[i])+abs(tx[i]))*wt+(abs(ty[i]-gy)+abs(tx[i]-gx))*dt);
}
cout<<min(driveTime,walkTime)<<endl;
}
3、調整隊形
題目
在幼兒園有n個小朋友排列為一個隊伍,從左到右一個挨著一個編號為(0~n-1)。其中有一些是男生,有一些是女生,男生用’B’表示,女生用’G’表示。小朋友們都很頑皮,當一個男生挨著的是女生的時候就會發生矛盾。作為幼兒園的老師,你需要讓男生挨著女生或者女生挨著男生的情況最少。你只能在原隊形上進行調整,每次調整隻能讓相鄰的兩個小朋友交換位置,現在需要儘快完成隊伍調整,你需要計算出最少需要調整多少次可以讓上述情況最少。例如:
GGBBG -> GGBGB -> GGGBB
這樣就使之前的兩處男女相鄰變為一處相鄰,需要調整隊形2次
輸入描述:
輸入資料包括一個長度為n且只包含G和B的字串.n不超過50.
輸出描述:
輸出一個整數,表示最少需要的調整隊伍的次數
輸入例子:
GGBBG
輸出例子:
2
分析
事實上,最終的結果只有兩種可能,要麼是GGG…BBB,要麼是BBB…GGG。我們不妨考慮B,我們可以事先求出B全部在左邊和全部在右邊兩種情況下的和。然後對當前每個B的下標求和,顯然我們每進行一次有用的調整必然使當前的和+1或者-1。那麼,我們只要計算出當前的和與最終結果的和的差就行了,由於最終結果有兩種情況,我們取最小值即可。
我的答案
#include<iostream>
#include<string>
using namespace std;
int main(){
string s;
cin>>s;
int cntB=0;
int valB=0;
for(int i=0;i<s.length();i++){
cntB+=s[i]=='B';
valB+=s[i]=='B'?i:0;
}
int leftB=valB-cntB*(cntB-1)/2;
int rightB=(2*s.length()-cntB-1)*cntB/2-valB;
cout<<min(leftB,rightB)<<endl;
}
4、消除重複元素
題目
小易有一個長度為n序列,小易想移除掉裡面的重複元素,但是小易想是對於每種元素保留最後出現的那個。小易遇到了困難,希望你來幫助他。
輸入描述:
輸入包括兩行:
第一行為序列長度n(1 ≤ n ≤ 50)
第二行為n個數sequencei,以空格分隔
輸出描述:
輸出消除重複元素之後的序列,以空格分隔,行末無空格
輸入例子:
9
100 100 100 99 99 99 100 100 100
輸出例子:
99 100
分析
從後向前先判重後加入即可。
我的答案
#include<iostream>
#include<string>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
int main(){
int n;
int a[1002]={0};
vector<int>v;
set<int>s;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=n-1;i>=0;i--){
if(s.find(a[i])==s.end()){
s.insert(a[i]);
v.push_back(a[i]);
}
}
cout<<v[v.size()-1];
for(int i=v.size()-2;i>=0;i--){
cout<<" "<<v[i];
}
cout<<endl;
5、魔力手環
題目
小易擁有一個擁有魔力的手環上面有n個數字(構成一個環),當這個魔力手環每次使用魔力的時候就會發生一種奇特的變化:每個數字會變成自己跟後面一個數字的和(最後一個數字的後面一個數字是第一個),一旦某個位置的數字大於等於100就馬上對100取模(比如某個位置變為103,就會自動變為3).現在給出這個魔力手環的構成,請你計算出使用k次魔力之後魔力手環的狀態。
輸入描述:
輸入資料包括兩行:
第一行為兩個整數n(2 ≤ n ≤ 50)和k(1 ≤ k ≤ 2000000000),以空格分隔
第二行為魔力手環初始的n個數,以空格分隔。範圍都在0至99.
輸出描述:
輸出魔力手環使用k次之後的狀態,以空格分隔,行末無空格。
輸入例子:
3 2
1 2 3
輸出例子:
8 9 7
分析
矩陣快速冪模板題。。。
我的答案
#include<iostream>
#include<cstring>
using namespace std;
#define MAX 52 #define MOD 100 struct Matrix{
int n,m;
int a[MAX][MAX];
void clear(){
n=m=0;
memset(a,0,sizeof a);
}
Matrix operator +(const Matrix &b)const{
Matrix tmp;
tmp.n=n;
tmp.m=m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
tmp.a[i][j]=(a[i][j]+b.a[i][j])%MOD;
return tmp;
}
Matrix operator *(const Matrix &b)const{
Matrix tmp;
tmp.clear();
tmp.n=n;
tmp.m=b.m;
for(int i=0;i<n;i++)
for(int j=0;j<b.m;j++)
for(int k=0;k<m;k++)
tmp.a[i][j]=(tmp.a[i][j]+a[i][k]*b.a[k][j])%MOD;
return tmp;
}
void print()const{
for(int i=0;i<n;i++){
for(int j=0;j<m-1;j++)
cout<<a[i][j]<<" ";
cout<<a[i][m-1]<<endl;
}
}
Matrix operator ^(int k)const{
Matrix tmp,mul;
tmp.clear();
mul.clear();
mul.n=mul.m=tmp.m=tmp.n=n;
mul=mul+(*this);
for(int i=0;i<n;i++)
tmp.a[i][i]=1;
while(k>0){
if(k&1){
tmp=tmp*mul;
}
k>>=1;
mul=mul*mul;
}
return tmp;
}
Matrix transpose(){
Matrix tmp;
tmp.n=m;
tmp.m=n;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
tmp.a[j][i]=a[i][j];
return tmp;
}
};
int n,k;
int main(){
Matrix arr;
cin>>n>>k;
arr.m=1;
arr.n=n;
for(int i=0;i<n;i++)
cin>>arr.a[i][0];
Matrix mul;
mul.clear();
mul.n=mul.m=n;
for(int i=0;i<n;i++){
mul.a[i][i]=1;
mul.a[i][(i+1)%n]=1;
}
arr=(mul^k)*arr;
arr.transpose().print();
}
6、工作安排
題目
現在有n位工程師和6項工作(編號為0至5),現在給出每個人能夠勝任的工作序號表(用一個字串表示,比如:045,表示某位工程師能夠勝任0號,4號,5號工作)。現在需要進行工作安排,每位工程師只能被安排到自己能夠勝任的工作當中去,兩位工程師不能安排到同一項工作當中去。如果兩種工作安排中有一個人被安排在的工作序號不一樣就被視為不同的工作安排,現在需要計算出有多少種不同工作安排計劃。
輸入描述:
輸入資料有n+1行:
第一行為工程師人數n(1 ≤ n ≤ 6)
接下來的n行,每行一個字串表示第i(1 ≤ i ≤ n)個人能夠勝任的工作(字串不一定等長的)
輸出描述:
輸出一個整數,表示有多少種不同的工作安排方案
輸入例子:
6
012345
012345
012345
012345
012345
012345
輸出例子:
720
分析
空間不大,直接dfs就行了。
我的答案
#include<iostream>
#include<cstring>
using namespace std;
bool can[6][6];
bool vis[6];
int n;
int dfs(int now){
int ans=0;
for(int i=0;i<6;i++){
if(can[now][i]&&!vis[i]){
vis[i]=true;
if(now==0)
ans++;
else
ans+=dfs(now-1);
vis[i]=false;
}
}
return ans;
}
int main(){
cin>>n;
memset(can,0,sizeof can);
memset(vis,0,sizeof vis);
for(int i=0;i<n;i++){
string s;
cin>>s;
for(int j=0;j<s.length();j++)
can[i][s[j]-'0']=true;
}
cout<<dfs(n-1)<<endl;
}
7、集合
題目
小易最近在數學課上學習到了集合的概念,集合有三個特徵:1.確定性 2.互異性 3.無序性.
小易的老師給了小易這樣一個集合:
S = { p/q | w ≤ p ≤ x, y ≤ q ≤ z }
需要根據給定的w,x,y,z,求出集合中一共有多少個元素。小易才學習了集合還解決不了這個複雜的問題,需要你來幫助他。
輸入描述:
輸入包括一行:
一共4個整數分別是w(1 ≤ w ≤ x),x(1 ≤ x ≤ 100),y(1 ≤ y ≤ z),z(1 ≤ z ≤ 100).以空格分隔
輸出描述:
輸出集合中元素的個數
輸入例子:
1 10 1 1
輸出例子:
10
分析
題意就是給分數判重,顯然我們不能直接算,因為浮點數是不精確的,建個結構體儲存最簡分數,然後丟進set裡就好了。
我的答案
#include<iostream>
#include<cstring>
#include<set>
using namespace std;
struct Frac{
int p,q;
bool operator<(const Frac &frac)const{
if(p!=frac.p)
return p<frac.p;
if(q!=frac.q)
return q<frac.q;
return false;
}
};
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int main(){
int w,x,y,z;
set<Frac>s;
cin>>w>>x>>y>>z;
Frac f;
for(int i=w;i<=x;i++){
for(int j=y;j<=z;j++){
int div=gcd(i,j);
f.p=i/div;
f.q=j/div;
s.insert(f);
}
}
cout<<s.size()<<endl;
}
8、奇怪的表示式求值
題目
常規的表示式求值,我們都會根據計算的優先順序來計算。比如/的優先順序就高於±。但是小易所生活的世界的表示式規則很簡單,從左往右依次計算即可,而且小易所在的世界沒有除法,意味著表示式中沒有/,只有(+, - 和 )。現在給出一個表示式,需要你幫忙計算出小易所在的世界這個表示式的值為多少
輸入描述:
輸入為一行字串,即一個表示式。其中運算子只有-,+,*。參與計算的數字只有0~9.
保證表示式都是合法的,排列規則如樣例所示。
輸出描述:
輸出一個數,即表示式的值
輸入例子:
3+5*7
輸出例子:
56
分析
小模擬。。。
我的答案
#include<iostream>
#include<string>
using nam