牛客網—網易2019實習生招聘程式設計題
第一題 牛牛找工作
為了找到自己滿意的工作,牛牛收集了每種工作的難度和報酬。牛牛選工作的標準是在難度不超過自身能力值的情況下,牛牛選擇報酬最高的工作。在牛牛選定了自己的工作後,牛牛的小夥伴們來找牛牛幫忙選工作,牛牛依然使用自己的標準來幫助小夥伴們。牛牛的小夥伴太多了,於是他只好把這個任務交給了你。
輸入描述:
每個輸入包含一個測試用例。
每個測試用例的第一行包含兩個正整數,分別表示工作的數量N(N<=100000)和小夥伴的數量M(M<=100000)。
接下來的N行每行包含兩個正整數,分別表示該項工作的難度Di(Di<=1000000000)和報酬Pi(Pi<=1000000000)。
接下來的一行包含M個正整數,分別表示M個小夥伴的能力值Ai(Ai<=1000000000)。
保證不存在兩項工作的報酬相同。
輸出描述:
對於每個小夥伴,在單獨的一行輸出一個正整數表示他能得到的最高報酬。一個工作可以被多個人選擇。
示例1
輸入
3 3
1 100
10 1000
1000000000 1001
9 10 1000000000
輸出
100
1000
1001
解題思路:
最直觀的方法是使用雙重迴圈,但是時間複雜度為O(n^2),超時了。。。。
將工作難度和報酬根據難度進行排序,然後將報酬更新為當前難度下的最高報酬,因為難度和報酬不一定是成正比的,所以為得到更高的報酬,無法根據初始難度來獲得最高的報酬。
如初始難度和報酬排序為:
1 100
2 10
3 1000
10 500
更新後為:
1 100
2 100
3 1000
4 1000
然後選擇和自身能力最接近的工作難度所得報酬即為所求。
程式碼:
import java.util.*;
public class Main{
public static void main(String[] args){
//使用Scanner類進行輸入
Scanner in = new Scanner(System.in);
int workNum = in.nextInt();
int partnerNum = in.nextInt();
//將工作難度和報酬儲存到work二維陣列中
int [][] work = new int[workNum][2];
for(int i = 0;i < workNum;i++){
work[i][0] = in.nextInt();
work[i][1] = in.nextInt();
}
//儲存夥伴能力到partnerAi陣列中
int[] partnerAi = new int[partnerNum];
for(int i = 0;i<partnerNum;i++){
partnerAi[i] = in.nextInt();
}
//根據工作難度,對工作進行排序,這裡使用jdk1.8的lambda
Arrays.sort(work, ((o1, o2) -> o1[0]-o2[0]));
//更新工作報酬為當前難度最高報酬,並將工作放置到TreeMap中
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
map.put(work[0][0], work[0][1]);
for(int i = 1;i<workNum;i++){
//更新報酬
work[i][1] = Math.max(work[i-1][1], work[i][1]);
map.put(work[i][0], work[i][1]);
}
for(int i = 0;i<partnerNum;i++){
//TreeMap中的floorKey方法即是找到與當前key最接近的key
Integer k = map.floorKey(partnerAi[i]);
if(k != null)
System.out.println(map.get(k));
else
System.out.println(0);
}
}
}
第二題 被3整除
小Q得到一個神奇的數列: 1, 12, 123,…12345678910,1234567891011…。
並且小Q對於能否被3整除這個性質很感興趣。
小Q現在希望你能幫他計算一下從數列的第l個到第r個(包含端點)有多少個數可以被3整除。
輸入描述:
輸入包括兩個整數l和r(1 <= l <= r <= 1e9), 表示要求解的區間兩端。
輸出描述:
輸出一個整數, 表示區間內能被3整除的數字個數。
示例1:
輸入
2 5
輸出
3
說明
12, 123, 1234, 12345…
其中12, 123, 12345能被3整除。
解題思路:
一個數字,判斷是否被3整除,可以將每個位上的數字相加,如果和可以被3整除,那麼這個數字便可以被3整除,這種方法最直觀,但是同樣超時了。。。。
通過找規律可以看出,這個數列中的每個數是有1,2,3….組成的,所以要判斷第i個數是否能被3整除,可以通過(1+2+3…+i)/3是否等於0來判斷,這裡有1,2,3,4…除以3取餘分別為1,2,0,1,2,0,1,2,0
這個數列是否能被3整除為:false,true,true,false,true,true,false,true,true…..即每3個數字一個迴圈。
所以前i個數字中:
如果i%3 == 0,則能被3整除的數字個數為i/3 *2;
如果i%3 == 1,則能被3整除的數字的個數為i/3 * 2;
如果i%3 == 2,則能被3整除的數字的個數為i/3 *2 +1;
import java.util.*;
public class Main{
public static void main(String[] args){
//輸入資料
Scanner in = new Scanner(System.in);
int l = in.nextInt();
int r = in.nextInt();
System.out.println(calThree(r) - calThree(l-1));
}
public static int calThree(int num){
if(num%3 == 1 || num%3==0){
return num/3*2;
}
else
return num/3*2+1;
}
}
第三題 安置路燈
小Q正在給一條長度為n的道路設計路燈安置方案。
為了讓問題更簡單,小Q把道路視為n個方格,需要照亮的地方用’.’表示, 不需要照亮的障礙物格子用’X’表示。
小Q現在要在道路上設定一些路燈, 對於安置在pos位置的路燈, 這盞路燈可以照亮pos - 1, pos, pos + 1這三個位置。
小Q希望能安置儘量少的路燈照亮所有’.’區域, 希望你能幫他計算一下最少需要多少盞路燈。
輸入描述:
輸入的第一行包含一個正整數t(1 <= t <= 1000), 表示測試用例數
接下來每兩行一個測試資料, 第一行一個正整數n(1 <= n <= 1000),表示道路的長度。
第二行一個字串s表示道路的構造,只包含’.’和’X’。
輸出描述:
對於每個測試用例, 輸出一個正整數表示最少需要多少盞路燈。
示例1
輸入
2
3
.X.
11
…XX….XX
輸出
1
3
解題思路:
每個路燈可以照亮三個位置,所以,只要每3個位置中有一個位置為“.”,即需要照亮,就需要一盞燈,所以設定一個指標,當指向“X”時跳過,當指向“.”時,無論其後面的兩個位置是什麼,均需要設定一盞燈,並將指標指向的位置+3。
程式碼:
import java.util.*;
public class Main{
public static void main(String[] args){
//輸入,num代表測試用例的數量
Scanner in = new Scanner(System.in);
int num = in.nextInt();
while(num > 0){
int roadLen = in.nextInt(); //路長
String road = in.next(); //需要照亮的路況
int re = 0; //需要路燈的數量
int i = 0; //指標指向每一格
while(i < roadLen){
if(road.charAt(i) == 'X')
i++;
else{
re++;
i +=3;
}
}
num--;
System.out.println(re);
}
}
}
第四題 迷路的牛牛
牛牛去犇犇老師家補課,出門的時候面向北方,但是現在他迷路了。雖然他手裡有一張地圖,但是他需要知道自己面向哪個方向,請你幫幫他。
輸入描述:
每個輸入包含一個測試用例。
每個測試用例的第一行包含一個正整數,表示轉方向的次數N(N<=1000)。
接下來的一行包含一個長度為N的字串,由L和R組成,L表示向左轉,R表示向右轉。
輸出描述:
輸出牛牛最後面向的方向,N表示北,S表示南,E表示東,W表示西。
示例1
輸入
3
LRR
輸出
E
解題思路:
向右轉為順時針轉1步,向左轉為逆時針轉1步,即順時針轉3步,那麼將東西南北儲存為字串str = “NESW”,使用reCount記錄每次順時針轉動的步數,每向右轉加1,每向左轉加3,每4步一個迴圈,所以最後需要reCount%4。
程式碼:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
//轉彎的次數
int n = in.nextInt();
//記錄東西南北方向的字串
String str = "NESW";
//記錄每次轉彎的方向
String dir = in.next();
//記錄順時針轉動的次數
int reCount = 0;
for(int i = 0;i<n;i++){
if(dir.charAt(i) == 'L')
reCount += 3;
else
reCount++;
}
reCount = reCount%4;
System.out.println(str.charAt(reCount));
}
}
第五題 數對
牛牛以前在老師那裡得到了一個正整數數對(x, y), 牛牛忘記他們具體是多少了。
但是牛牛記得老師告訴過他x和y均不大於n, 並且x除以y的餘數大於等於k。
牛牛希望你能幫他計算一共有多少個可能的數對。
輸入描述:
輸入包括兩個正整數n,k(1 <= n <= 10^5, 0 <= k <= n - 1)。
輸出描述:
對於每個測試用例, 輸出一個正整數表示可能的數對數量。
示例1
輸入
5 2
輸出
7
說明
滿足條件的數對有(2,3),(2,4),(2,5),(3,4),(3,5),(4,5),(5,3)
解題思路:
首先最簡單易想到的方法就是暴力破解,但又超時類 。。。。。
當k==0時,那麼下x,y取<=n的正整數均可,所以滿足的數對的數量為n*n;
當k>0時,那麼x的取值範圍為[1,n],可以將n分成n/y個整區間,每個區間內分別為(1,2…y-2,y-1,y)(y+1, y+2….2y)……
那麼對於在每個區間內的數可以代表x,每個區間上除去最後一個數,第i個數%y總是等於i的,所以每個區間上%y後>=k的數的個數為y-k,所以在整區間內,x的個數為n/y*(y-k)。
當n%y>=k時,最後的存在的非整區間中也有滿足條件的值,非整區間的數字的個數為n%y,其中除以y取餘後>=k的個數為n%y-(k-1)個。
java中int型別佔32位,所以取值範圍為-2^31~2^31-1即(-2147483648,2147483647)
程式碼實現:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
//雖然n的取值範圍為1~10^5次方,但是多種情況相加後得到的總數可能會超過2^31-1,所以這裡直接使用Long型別
Long n = in.nextLong();
Long k = in.nextLong();
Long reNum = 0L;
/*暴力破解,超時了
for(int i=1; i<n+1;i++){
for(int j=1;j<n+1;j++){
if(i%j>=k)
reNum++;
}
}*/
Long y;
if(k==0){
reNum = n*n;
}
else{
//既然餘數為k,那麼y的取值範圍應為[k+1, n]
for(y = k+1;y<=n;y++){
reNum += (n/y) * (y-k);
if(n%y>=k){
reNum += n%y - k + 1;
}
}
}
System.out.println(reNum);
}
}
第六題 矩形重疊
平面內有n個矩形, 第i個矩形的左下角座標為(x1[i], y1[i]), 右上角座標為(x2[i], y2[i])。
如果兩個或者多個矩形有公共區域則認為它們是相互重疊的(不考慮邊界和角落)。
請你計算出平面內重疊矩形數量最多的地方,有多少個矩形相互重疊。
輸入描述:
輸入包括五行。
第一行包括一個整數n(2 <= n <= 50), 表示矩形的個數。
第二行包括n個整數x1[i](-10^9 <= x1[i] <= 10^9),表示左下角的橫座標。
第三行包括n個整數y1[i](-10^9 <= y1[i] <= 10^9),表示左下角的縱座標。
第四行包括n個整數x2[i](-10^9 <= x2[i] <= 10^9),表示右上角的橫座標。
第五行包括n個整數y2[i](-10^9 <= y2[i] <= 10^9),表示右上角的縱座標。
輸出描述:
輸出一個正整數, 表示最多的地方有多少個矩形相互重疊,如果矩形都不互相重疊,輸出1。
示例1
輸入
2
0 90
0 90
100 200
100 200
輸出
2
解題思路:
通過暴力判斷所有矩形的點分別在多少個矩形內(點在矩形內,則說明有重疊區域),從中選擇最大的,即為所求。
程式碼實現:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
//左下角橫座標
int[] x1 = new int[n];
//左下角縱座標
int[] y1 = new int[n];
//右上角橫座標
int[] x2 = new int[n];
//右上角縱座標
int[] y2 = new int[n];
//存放矩形點的橫座標
ArrayList<Integer> xx = new ArrayList<Integer>();
//存放矩形點的縱座標
ArrayList<Integer> yy = new ArrayList<Integer>();
//輸入左下角橫座標,並存入xx
for(int i = 0;i<n;i++){
x1[i] = in.nextInt();
xx.add(x1[i]);
}
//輸入左下角縱座標,並存入yy
for(int i = 0;i<n;i++){
y1[i] = in.nextInt();
yy.add(y1[i]);
}
//輸出右上角橫座標,並存入xx
for(int i = 0;i<n;i++){
x2[i] = in.nextInt();
xx.add(x2[i]);
}
//輸入右上角縱座標,並存入yy
for(int i = 0;i<n;i++){
y2[i] = in.nextInt();
yy.add(y2[i]);
}
//點在矩形內最多的個數
int re = 0;
//迴圈遍歷,外兩圈為矩形的點,最內圈為迴圈每個矩形
for(int x:xx){
for(int y:yy){
int count = 0;
for(int i=0;i<n;i++){
//將矩形中的點分別與每個矩形的左下角和右上角比較,判斷是否在矩形內
if(x>x1[i]&&y>y1[i]&&x<=x2[i]&&y<=y2[i])
count++;
}
re = Math.max(re,count);
}
}
System.out.println(re);
}
}
第七題 牛牛的鬧鐘
牛牛總是睡過頭,所以他定了很多鬧鐘,只有在鬧鐘響的時候他才會醒過來並且決定起不起床。從他起床算起他需要X分鐘到達教室,上課時間為當天的A時B分,請問他最晚可以什麼時間起床
輸入描述:
每個輸入包含一個測試用例。
每個測試用例的第一行包含一個正整數,表示鬧鐘的數量N(N<=100)。
接下來的N行每行包含兩個整數,表示這個鬧鐘響起的時間為Hi(0<=A<24)時Mi(0<=B<60)分。
接下來的一行包含一個整數,表示從起床算起他需要X(0<=X<=100)分鐘到達教室。
接下來的一行包含兩個整數,表示上課時間為A(0<=A<24)時B(0<=B<60)分。
資料保證至少有一個鬧鐘可以讓牛牛及時到達教室。
輸出描述:
輸出兩個整數表示牛牛最晚起床時間。
示例1
輸入
3
5 0
6 0
7 0
59
6 59
輸出
6 0
解題思路1:
將鬧鈴排序後並與牛牛到達教室的所需時間相加,求得其中最接近上課時間的值即可。
程式碼實現1:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
//設定存放鬧鐘時間的Clock陣列
Clock[] clock = new Clock[n];
//輸入鬧鐘時間
for(int i = 0;i<n;i++){
int H = in.nextInt();
int B = in.nextInt();
Clock c = new Clock(H,B);
clock[i] = c;
}
//xTime為達到教室所需時間,將其同樣轉化為Clock類
int xTime = in.nextInt();
Clock x = new Clock(xTime/60,xTime%60);
//輸入上課時間,並轉化為Clock類
int hClass = in.nextInt();
int bClass = in.nextInt();
Clock goClass = new Clock(hClass,bClass);
//對鬧鐘時間進行排序
Arrays.sort(clock);
for(int i = n-1;i>-1;i--){
//倒序比較鬧鐘時間+準備時間和上課時間比較 if(clock[i].add(x).compareTo(goClass)==0||clock[i].add(x).compareTo(goClass)==-1){
System.out.println(clock[i].H + " " + clock[i].B);
break;
}
}
}
}
//編寫Clock類並實現Comparable介面,並重寫compareTo方法,注意的是在介面名Comparable後使用泛型
class Clock implements Comparable<Clock>{
int H;
int B;
Clock(int H, int B){
this.H = H;
this.B = B;
}
//Clock的方法,進行時間相加
public Clock add(Clock c){
int reB = (c.B + this.B)%60;
int reH = c.H + this.H + (c.B + this.B)/60;
Clock re = new Clock(reH, reB);
return re;
}
@Override
//通過比較時針和分針,判斷時間大小重寫compareTo方法
public int compareTo(Clock c) {
if(this.H > c.H)
return 1;
if(this.H == c.H)
if(this.B > c.B)
return 1;
else{
if(this.B == c.B)
return 0;
else
return -1;
}
else
return -1;
}
}
解題思路2:
將時間都轉換為分鐘的形式,方便進行計算,然後根據上課時間和準備時間計算出牛牛最晚起床時間,在鬧鐘中通過二分查詢演算法找出上界。
程式碼實現2:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] clock = new int[n];
for(int i = 0;i<n;i++){
clock[i] = in.nextInt()*60 + in.nextInt();
}
Arrays.sort(clock);
int xTime = in.nextInt();
int classTime = in.nextInt()*60+ in.nextInt();
int getUp = classTime - xTime;
System.out.println(bs(clock, getUp)/60 + " " + bs(clock, getUp)%60);
}
//二分查詢求上界
public static int bs(int[] c, int t){
int len = c.length;
int left = 0;
int right = len;
while(left < right){
int mid = (left+right)/2;
if(c[mid]>t)
right = mid;
else
left = mid+1;
}
return c[left-1];
}
}
第八題 牛牛的揹包
牛牛準備參加學校組織的春遊, 出發前牛牛準備往揹包裡裝入一些零食, 牛牛的揹包容量為w。
牛牛家裡一共有n袋零食, 第i袋零食體積為v[i]。
牛牛想知道在總體積不超過揹包容量的情況下,他一共有多少種零食放法(總體積為0也算一種放法)。
輸入描述:
輸入包括兩行
第一行為兩個正整數n和w(1 <= n <= 30, 1 <= w <= 2 * 10^9),表示零食的數量和揹包的容量。
第二行n個正整數v[i](0 <= v[i] <= 10^9),表示每袋零食的體積。
輸出描述:
輸出一個正整數, 表示牛牛一共有多少種零食放法。
示例1
輸入
3 10
1 2 4
輸出
8
說明
三種零食總體積小於10,於是每種零食有放入和不放入兩種情況,一共有2*2*2 = 8種情況。
解題思路:
對於每種零食,有兩種選擇,放入或者不放入,所以通過使用深度優先搜尋,遞迴實現。
程式碼實現:
import java.util.*;
public class Main{
static int count = 0;
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt(); //零食數量
int w = in.nextInt(); //揹包體積
int[] v = new int[n]; //每個零食的體積
long sumv = 0L; //零食體積之和,注意雖然每個零食的體積<2*10^9,但是所有零食之和很可能就超過int表示的數值範圍了,所以使用long型別
for(int i=0;i<n;i++){
v[i] = in.nextInt();
sumv += v[i];
}
//當所有零食的體積小於揹包體積時,所有零食都可以放入
if(sumv <= w)
System.out.println((int)Math.pow(2,n));
else{
dfs(0,0,w,v,n);
System.out.println(count+1);
}
}
/*深度搜索,
sum為當前待要加入揹包中零食的體積,同樣要使用long型別;
cur為當前正在考慮的是否能夠加入到揹包中零食的號碼;
w為揹包的體積;
v為零食的體積;
n為零食的數量;
*/
public static void dfs(long sum, int cur, int w, int[] v, int n){
//在當前考慮的零食的號碼小於總零食的數量才能進行
if(cur < n){
//如果待加入到揹包中的零食體積和超過了揹包的體積,則直接返回
if(sum > w)
return ;
//當前號碼為cur的零食不加入到揹包中,所以cur+1,但是sum不變
dfs(sum, cur+1,w,v,n);
//如果待加入到揹包中的零食體積+當前零食體積<=揹包體積,便可以將當前零食加入
if(sum + v[cur]<=w){
count ++;
//cur號零食已經加入揹包,分析下一個號碼的零食情況
dfs(sum+v[cur],cur+1,w,v,n);
}
}
}
}