網易-2020秋招-筆試題剖析【5道算法題】,限時120分鐘。
讓我們一起來看看這些題吧!
題一:最小數位和
【題目描述】
定義S(n),表示n在十進制下的各位數字和。
現在給定一個x,請你求出最小正整數n,滿足x≤S(n).
【輸入描述】
第一行數據組數T,對於每組數據,一行一個數字x。
1≤x≤10^5,1≤T≤10
【輸出描述】
對於每組數據,一行一個整數表示最小的n。
【示例】
示例1:
輸入:
2
7
9
輸出:
7
9
示例2:
輸入:
2
13
18
輸出:
49
99
【解決思路及要點】
- 【貪心】n要儘可能小,說明位數要儘可能少,在十進制中9最大,所以對x除9,就可以得到n中9的個數,n的最高位就是餘數。(滿足n最高位最小,位數最小)
- 可以直接從最高位開始一位一位的輸出,先輸出餘數,再輸出x/9個9不需要再整體轉換爲數字。
【解決代碼】
public class Solution1 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int T=sc.nextInt();
while(T-->0){
int x=sc.nextInt();
solve(x);
}
}
public static void solve(int x){
//餘數做最高位
if(x%9>0){
System.out.print(x%9);
}
x-=x%9;
//x/9就是n中9的個數
for(int i=1;i<=x/9;i++){
System.out.print("9");
}
System.out.print("\n");
}
}
【題目剖析】
- 整個題的思想就是貪心,要n最小,需要滿足什麼樣的條件。
- 這題屬於思維類的題目,應該是比較簡單,但如果一開始的思路就往復雜方向去了,這個題可能會花費較多的時間,而且有做不出的風險。
- 所以寫代碼前先想想,有沒有更簡單,更準確的做法。
題二:喫葡萄
【題目描述】
有三種葡萄,每種分別有a,b,c顆。有三個人,第一個人只吃第1,2種葡萄,第二個人只吃第2,3種葡萄,第三個人只吃第1,3種葡萄。
適當安排三個人使得喫完所有的葡萄,並且三個人中喫的最多的那個人喫得儘量少。
【輸入描述】
第一行數字T,表示數據組數。
接下來T行,每行三個數a,b,c
1≤a,b,c≤10^18,1≤T≤10
【輸出描述】
對於每組數據,輸出一行一個數字表示三個人中喫的最多的那個人喫的數量。
【示例】
示例1:
輸入:
2
1 2 3
1 2 6
輸出:
2
3
示例2:
輸入:
1
12 13 11
輸出:
12
【解決思路及要點】
- 如果兩種較少葡萄的和比葡萄最多的一半要多,那麼可以實現平分。【此時三人平分能夠使得喫的最多的那個人喫得儘量少】
- 如果兩種較少葡萄的和比葡萄最多的一半要少,那麼結果是最多葡萄的一半。
- 注意要使用向上取整。(一顆葡萄也要單獨喫一次)
- 也可以看成是三個人分別站在三角形的頂點,假設三角形兩個短邊是a,b,長邊是c。則,若兩短邊之和大於等於長邊的一半,可實現總數平分;反之,則結果爲長邊的一半。
【解決代碼】
public class Solution2 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int T=sc.nextInt();
while(T-->0){
long a,b,c;
a=sc.nextLong();
b=sc.nextLong();
c=sc.nextLong();
System.out.println(solve(a,b,c));
}
}
public static long solve(long a,long b,long c){
long maxx=Math.max(Math.max(a,b),c);
long minn=Math.min(Math.min(a,b),c);
long mid=a+b+c-maxx-minn;
if(minn+mid>=maxx/2){
return up(a+b+c,3);
}else{
return up(maxx,2);
}
}
//除法向上取整
private static long up(long a,long b){
return a%b>0?a/b+1:a/b;
}
}
【題目剖析】
- 總體來說,也是貪心的思想。
- 這也是一道思維題,需要能抓住合適能夠平分,何時是最大值的一半。
- 類比三角形三邊是一種很好的思維。
- 如果思維不到位的話,容易花了時間而做不對。
- 如果掌握了這個思路,就可以非常快速的做出了。
題三:圓環切割
【題目描述】
小易有n個數字排成一個環,你能否將它們分成連續的兩個部分(即在環上必須連續),使得兩部分的和相等?
【輸入描述】
第一行數據組數T,對於每組數據
第一行數字n,表示數字個數
接下來一行n個數,按順序給出環上的數字。
2≤n≤100000,1≤Ai≤10^9
【輸出描述】
對於每組數據,一行輸出YES/NO
【示例】
示例1:
輸入:
1
6
1 2 3 4 5 6
輸出:
NO
示例2:
輸入:
1
4
4 4 5 3
輸出:
YES
【解決思路及要點】
- 維護一個前綴和數組,將每個前綴和與圓環和的一半做差,如果存在一個前綴和和這個差相等,那麼就可以分割。【具體的原理自己在腦海中構造個圓環想一下】這裏利用的是圓環起點可變的特性。
【解決代碼】
public class Solution3 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int T=sc.nextInt();
while(T-->0){
int n=sc.nextInt();
int[] pre=new int[n];
for(int i=0;i<n;i++){
int x=sc.nextInt();
pre[i]=(i>0)?pre[i-1]+x:x;
}
if(solve(pre)){
System.out.println("YES");
}else{
System.out.println("NO");
}
}
}
public static boolean solve(int[] pre){
int n=pre.length;
//奇數不可能
if((pre[n-1]&1)>0){
return false;
}
Set<Integer> set=new HashSet<>();
for(int i=0;i<n;i++){
//得到pre[i]與圓環和一半的差值
int s=pre[i]-pre[n-1]/2;
//若剛好有一個前綴和與這個差值相等,說明可以從前綴中減去,從而構成兩部分相等
if(set.contains(s)){
return true;
}
//將這個前綴和存入set集合
set.add(pre[i]);
}
return false;
}
}
【題目剖析】
- 這題思維性不是那麼強,算是一箇中等偏下的題,需要對前綴和應用比較熟悉。
- 這個題一步一步做,方法應該還有很多,總體而言不是很難。
題四:跳柱子
【題目描述】
小易有n根柱子,第i根柱子的高度爲hi。一開始小易站在第一根柱子上。小易能從第i根柱子跳到第j根柱子,當且僅當hj≤hi且1≤j−i≤k。其中k爲指定的一個數字。
另外小易擁有一次釋放超能力的機會。這個超能力能讓小易從柱子i跳到任意滿足1≤j−i≤k的柱子j而無視柱子高度的限制。
現在小易想知道,小易是否能到達第n根柱子。
【輸入描述】
第一行數據組數T
對於每組數據,第一行數字n,k接下來一行n個數字表示hi。
1≤n≤1000,1≤hi≤10^9,1≤T≤10,1≤k≤n
【輸出描述】
對於每組數據,輸出YES或NO
【示例】
示例1:
輸入:
1
5 3
6 2 4 3 8
輸出:
YES
示例2:
輸入:
1
5 2
1 8 2 3 4
輸出:
NO
【解決思路及要點】
- 明顯可以使用dp。
- dp[i][0]表示不使用超能力的情況下是否能到達柱子。
- dp[i][1]表示使用超能力的情況下是否能到達柱子。
- 對每個柱子,枚舉前面的k個柱子,並更新dp即可。
- 具體細節見代碼。
public class Solution4 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int T=sc.nextInt();
while(T-->0){
int n=sc.nextInt();
int k=sc.nextInt();
int[] height=new int[n];
for(int i=0;i<n;i++){
height[i]=sc.nextInt();
}
if(solve(height,k)){
System.out.println("YES");
}else{
System.out.println("NO");
}
}
}
public static boolean solve(int[] height,int k){
int n=height.length;
boolean[][] dp=new boolean[n][2];
//初始條件
dp[0][0]=dp[0][1]=true;
for(int i=0;i<n;i++){
for(int j=i-1;j>=0 && j>=i-k;j--){
//如果第j根柱子不使用超能力能達到
if(dp[j][0]){
//那麼使用也是肯定可以的
dp[j][1]=true;
//如果距離和高度都滿足條件,那麼第i根柱子在不使用超能力的情況下也是可以達到的
if(height[j]>=height[i]){
dp[i][0]=true;
}
}
//如果第j根柱子使用超能力能達到
if(dp[j][1]){
//那麼如果滿足條件下,第i根柱子在已經使用了超能力的情況下,能夠達到
if(height[j]>=height[i]){
dp[i][1]=true;
}
}
}
}
//最終的答案是,在允許使用超能力的情況下,能否到達最後一根柱子
return dp[n-1][1];
}
}
【題目剖析】
- 算是比較經典的dp,對dp熟悉的話,可以很快的做出。
- 整體來說,這題不是很難。
- 應該可以在比較快的時間內做出來。
題五:乘積
【題目描述】
小易給定你一個長度爲n的正整數序列Ai,你每次可以使用1的代價將某個數加一或者減一,你希望用最少的代價使得所有數的乘積等於B,求最小代價(操作結束後每個數也必須是正整數)。
【輸入描述】
第一行數字n, B,表示序列長度和目標乘積。
接下來一行n個正整數表示初始序列。
1≤n≤10^3 , 1≤B≤ 10^5,1≤Ai≤ 10^5.
【輸出描述】
一行一個數字表示答案
【示例】
示例1:
輸入:
5 12
1 3 9 2 6
輸出:
10
說明:
把3變爲1需要2的代價,把9變爲1需要8的代價,總代價爲10。
示例2:
輸入:
3 15
3 8 7
輸出:
9
說明:
把8變爲5需要3的代價,把7變爲1需要6的代價,總代價爲9。
【解決思路及要點】
- dp解決,dp[i][j]表示表示讓前i個數字乘積爲j的最小代價。
- 需要大量用到數的因子,故生成一個從1-B的所有數的因子的集合。
- 具體轉移方程和其它細節在註釋中。
public class Solution5 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int B=sc.nextInt();
int[] a=new int[n+1];
for(int i=1;i<=n;i++){
a[i]=sc.nextInt();
}
System.out.println(solve(a,B));
}
public static int solve(int[] a,int B){
int n=a.length-1;
//該集合是1-B的所有數的因子的集合
List<List<Integer>> v=new ArrayList<>();
//初始化集合
for(int i=0;i<=B;i++){
v.add(new ArrayList<>());
}
//得到1-B所有數的因子的集合
for(int i=1;i<=B;i++){
for(int j=i;j<=B;j+=i){
v.get(j).add(i);
}
}
//m爲B的因子的個數
int m=v.get(B).size();
int[] num=new int[B+1];
//從前往後記錄B的因子的下標【等於可以從一個因子得到相應的下標,也可以用哈希表實現】
for(int i=0;i<m;i++){
num[v.get(B).get(i)]=i;
}
//dp數組
int[][] dp=new int[n+1][m];
//初始化dp[0]爲最大值
for(int i=1;i<m;i++){
dp[0][i]= (int) 1e9;
}
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
//初始化dp[i][j]爲最大值
dp[i][j]= (int) 1e9;
//k遍歷B的第j個因子的所有因子
for(int k=0;k<v.get(v.get(B).get(j)).size();k++){
//dp[i-1][num[v.get(v.get(B).get(j)).get(k)]]是前i-1個數乘積爲B的第j個因子的第k個因子的最小代價
//Math.abs(a[i]-v.get(B).get(j)/v.get(v.get(B).get(j)).get(k))是 第i個數-B的第j個因子/B的第j個因子的第k個因子 的絕對值
//這兩個之和就是轉換的最小代價
dp[i][j]=Math.min(dp[i][j],dp[i-1][num[v.get(v.get(B).get(j)).get(k)]]
+Math.abs(a[i]-v.get(B).get(j)/v.get(v.get(B).get(j)).get(k)));
}
}
}
//最終答案是前n個數字乘積爲B的最小代價
return dp[n][m-1];
}
}
【題目剖析】
- 這個題開始我是完全沒有做出來,想到dp的思路,但始終找不到轉移的點。
- 這個題感覺還是比較難的dp了,估計在兩小時內做出的人比較少,這場比試真正拉開算法能力強的人與普通人差距的題目。
- 算是一個難度dp了。
該次筆試題感悟:
- 難度和順序基本一致。
- 這次筆試主要考察的是思維【貪心,dp出現次數多】,題目都簡短,但實際做卻感覺比較的難。
- 兩個小時的時間,前四題拿下的可能性很大,最後一題就看命了。
- 足以可以看出,算法還是不要只顧刷題,思維纔是最重要的點!