算法題解 - 牛客編程巔峯賽S1第2場 - 青銅&白銀組

A. 牛牛扔牌

題目描述

牛牛如今有 n 張撲克牌,每張撲克牌都有點數和花色兩部分組成。點數爲 ‘1’ - ‘9’ 的正整數,花色爲 'C', 'D', 'H', 'S' 其中的一個,分別表示梅花、方塊、紅桃、黑桃。如今牛牛想按必定的順序把這n張牌扔掉。扔牌順序的規則以下:java

  1. 若是如今還剩素數張牌,則將牌頂的牌扔掉
  2. 若是如今還剩非素數張牌,則將牌底的牌扔掉

牛牛想知道他的扔牌順序是什麼,請返回扔牌順序的字符串 數組

備註:spa

對於 100% 的數據,1 ≤ n ≤ 10。

示例1

輸入code

"3C8D6H3D"

輸出字符串

"3D3C8D6H"

說明get

開始 n = 4,爲非素數,扔掉牌底的牌 3D
n = 3,爲素數,扔掉牌頂的牌 3C
n = 2,爲素數,扔掉牌頂的牌 8D
n = 1,爲非素數,扔掉牌底的牌 6H

示例2

輸入數學

"8S8S8S8S8S8S8S"

輸出string

"8S8S8S8S8S8S8S"

說明it

由於全是8S,因此扔牌順序的每一張牌也都是8S

解法

思路分析

因爲 n 在 [1, 10] 上,故本題直接把全部素數列出來便可。沒什麼難度。class

時間複雜度:O(n)

空間複雜度:O(n)

代碼實現

/**
 * 
 * @param x string字符串 字符串從前到後分別是從上到下排列的n張撲克牌
 * @return string字符串
 */
public String Orderofpoker (String x) {
    int n = x.length() / 2;
    int top = 0, bot = x.length() - 2;
    String res = "";
    while(n > 0){
        if(n == 2 || n == 3 || n == 5 || n == 7){
            res += x.substring(top, top + 2);
            top += 2;
        }else{
            res += x.substring(bot, bot + 2);
            bot -= 2;
        }
        n--;
    }
    return res;
}

B. 瘋狂過山車

題目描述

今天牛牛去遊樂園玩過山車項目,他以爲過山車在上坡下坡的過程是很是刺激的,回到家以後就受到啓發,想到了一個問題。若是把整個過山車的軌道看成是一個長度爲 n 的數組 num,那麼在過山車上坡時數組中的值是呈現遞增趨勢的,到了最高點之後,數組中的值呈現遞減的趨勢,牛牛把符合這樣先增後減規律的數組定義爲金字塔數組,請你幫牛牛在整個 num 數組中找出長度最長的金字塔數組,若是金字塔數組不存在,請輸出 0。

備註:

1 <= n <= 1000000,且 num 數組中的數 0 <= num[i] <= 1000000。

示例1

輸入

4,[1,2,3,1]

輸出

4

示例2

輸入

5,[1,5,3,3,1]

輸出

3

解法一:直觀解法

思路分析

在遍歷的時候判斷是不是金字塔數組便可。當前數字 i 和前一個數字 j 比較有三種狀況:

  1. i > j,說明數組處於爬升階段,若 j 是低谷則對長度 len 初始化爲 1。len++。
  2. i = j,一定不是金字塔,初始化 len 爲 1。
  3. i < j,若以前處於爬升階段,說明是金字塔,len++,而且和金字塔最大長度 res 做比較。

時間複雜度:O(n)

空間複雜度:O(1)

代碼實現

/**
 * 
 * @param n int整型 
 * @param num int整型一維數組 
 * @return int整型
 */
public int getMaxLength (int n, int[] num) {
  // write code here
  int res = 0;
  int len = 1;
  boolean up = false;
  for(int i = 1; i < n; i++) {
    if(num[i] > num[i - 1]) {
      up = true;
      if(i > 1 && num[i - 1] < num[i - 2]) len = 1;
      len++;
    }else if(num[i] == num[i - 1]) {
      len = 1;
      up = false;
    }else {
      if(up) {
        len++;
        res = Math.max(len, res);
      }
    }
  }
  return res;
}

解法二:尋找金字塔頂法

思路分析

金字塔數組長度 = 爬升階段長度 + 1 (塔頂) + 降低階段長度。

降低階段至關於從右往左遍歷時的爬升階段

所以分別用 l 數組r 數組記錄從左到右從右到左的爬升階段長度。

若 l[i] 和 r[i] 都不爲 0,說明 i 是塔頂。

時間複雜度:O(n)

空間複雜度:O(n)

代碼實現

/**
 * 
 * @param n int整型 
 * @param num int整型一維數組 
 * @return int整型
 */
public int getMaxLength (int n, int[] num) {
  int res = 0;
  int[] l = new int[n];
  int[] r = new int[n];
  for(int i = 1; i < n; i++){
    if(num[i] > num[i - 1]) l[i] = l[i - 1] + 1;
  }
  for(int i = n - 2; i >= 0; i--){
    if(num[i] > num[i + 1]) r[i] = r[i + 1] + 1;
  }
  for(int i = 0; i < n; i++){
    if(l[i] != 0 && r[i] != 0) res = Math.max(res, l[i] + r[i] + 1);
  }
  return res;
}

C. 牛牛的棋盤

題目描述

牛牛最近在家裏看到一個棋盤,有 n m 個格子,在棋盤旁邊還放着 k 顆棋子,牛牛想把這 k 顆棋子所有放在 n m 的棋盤上,可是有一個限制條件:棋盤的第一行、第一列、最後一行和最後一列都必須有棋子。牛牛想知道這樣的棋子放法到底有多少種,答案須要對 1e9 + 7取模。

備註:

2 <= n, m <= 30; 1 <= k <=1000

示例1

輸入

2,3,1

輸出

0

說明

就1顆棋子,因此沒法知足條件。

示例2

輸入

2,2,2

輸出

2

說明

咱們能夠把第1顆棋子放在左上角,第2顆棋子放在右下角;也能夠把第1顆棋子放在右上角,第2顆棋子放在左下角。故而有2种放法。

解法:容斥原理

思路分析

本題須要具有高中的數學知識:容斥原理和排列組合數。涉及的數學公式會在題解中給出,若是有看不明白的地方能夠自行查詢相關數學概念。

記行 i 上無棋子的集合爲 $S_{ri}$, 列 j 上無棋子的集合爲 $S_{cj}$。根據容斥原理,有

$$ \begin{aligned} res &= ∣S_{r1} ∩ S_{rn} ∩ S_{c1} ∩ S_{cm}∣ \hspace{100cm}\\ &= all - |S_{r1} ∪ S_{rn} ∪ S_{c1} ∪ S_{cm}∣\\ &= all - \sum_{C \subseteq U}(-1)^{size(C) - 1}|\cap_{e \in C} e| \end{aligned} $$

其中, $U = \{S_{r1}, S_{rn}, S_{c1}, S_{cm}\}$。

因爲 $U$ 中只包含四個集合,所以能夠用 4 個 bit 分別表示是否交集合 $S_i$。

總共有 $2^4 - 1 = 15$ 種交集狀況,再加上 all ,共計 16 種狀況。

排列組合數能夠利用公式 $C_m^n = C_{m - 1}^n + C_{m - 1}^{n - 1}$ 迭代計算得出。

代碼實現

final int mod = (int)1e9 + 7;
/**
 * 
 * @param n int整型 
 * @param m int整型 
 * @param k int整型 
 * @return int整型
 */
public int solve (int n, int m, int k) {
  if(k < 2) return 0;
  int[][] C = initC();
  int res = 0;
  for(int i = 0; i < 16; i++){
    int r = n, c = m, cnt = 0;
    if((i & 1) != 0) {r--; cnt++;}
    if((i & 2) != 0) {r--; cnt++;}
    if((i & 4) != 0) {c--; cnt++;}
    if((i & 8) != 0) {c--; cnt++;}
    res = (res - ((cnt & 1) * 2 - 1) * C[r * c][k]) % mod;
  }
  return (res + mod) % mod;
}

public int[][] initC(){
  int n = 1000;
  int[][] C = new int[1001][1001];
  for(int i = 1; i <= n; i++) C[i][0] = C[i][i] = 1;
  for(int i = 2; i <= n; i++){
    for(int j = 1; j < i; j++){
      C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
    }
  }
  return C;
}

寫在最後

你們好,我是往西汪,一位堅持原創的新人博主。若是本文對你有幫助,請點贊、評論二連。你的支持是我創做路上的最大動力。謝謝你們!也歡迎來公衆號【往西汪】找我玩耍~

相關文章
相關標籤/搜索