藍橋杯——試題 算法訓練 Sereja and Squares

Java 代碼

```` package com.lzp.algorithmpractice.p4;

import java.util.Scanner;java

/**算法

  • @Author LZP測試

  • @Date 2021/2/17 16:18code

  • @Version 1.0遞歸

  • 動態規劃內存

  • 試題 算法訓練 Sereja and Squares資源

  • 資源限制字符串

  • 時間限制:4.0s 內存限制:256.0MBinput

  • 問題描述it

  •   Sereja在平面上畫了n個點,點i在座標(i,0)。而後,Sereja給每一個點標上了一個小寫或大寫英文字母。Sereja不喜歡字母"x",因此他不用它標記點。Sereja認爲這些點是漂亮的,當且僅當:

  •   ·全部的點能夠被分紅若干對,使得每一個點剛好屬於一一對之中。

  •   ·在每對點中,橫座標較小的會被標上小寫字母,較大的會被標上對應的大寫字母。

  •   ·若是咱們在每對點上畫一個正方形,其中已知的一對點會做爲正方形的相對的頂點,它們間的線段會成爲正方形的對角線,那麼在全部畫出的正方形中不會有相交或觸碰的狀況。

  •   小Petya擦掉了一些小寫字母和全部大寫字母,如今Sereja想知道有多少種方法來還原每一個點上的字母,使得還原後這些點是漂亮的。

  • 輸入格式

  •   第一行是一個整數n,表示點的個數。

  •   第二行是一個長度爲n的字符串,包含小寫字母和問號"?",是按照橫座標遞增的順序的每一個點的描述。問號表示這個點的字母被Petya擦掉了。保證輸入串不含字母"x"。

  • 輸出格式

  •   輸出答案對4294967296取模的值。若是沒有可行的方案,輸出0。

  • 樣例輸入

  • 4

  • a???

  • 樣例輸出

  • 50

  • 樣例輸入

  • 4

  • abc?

  • 樣例輸出

  • 0

  • 樣例輸入

  • 6

  • abc???

  • 樣例輸出

  • 1

  • 數據規模和約定

  •   20個測試點的n分別爲:

  •   5,10,20,50,100,

  •   200,500,1000,2000,5000,

  •   10000,20000,30000,40000,50000,

  •   60000,70000,80000,90000,100000.

  • 遞歸實現:

  • 不過效率過低

  • 使用棧,將其看成左右括號匹配的問題來實現,這是本題的重要解題思路

  • 本題適合用C++寫,C++的效率比Java高太多了
    */
    public class Main2 {

    private static long num = 0;

    private static long mod = 4294967296L;

    private static char[] arr = new char[100000 + 7];

    private static long[] dp = new long[100000 + 7];

    public static void main(String[] args) {
    Scanner input = new Scanner(System.in);
    int n = input.nextInt();
    char[] tempChar = input.next().toCharArray();
    for (int i = 0; i < tempChar.length; i++) {
    arr[i + 1] = tempChar[i];
    }

    // 沒有右括號 ,這一步必定不能漏
     dp[0] = 1;
     long start  = System.currentTimeMillis();
     f(arr, n);
     long end  = System.currentTimeMillis();
     System.out.println(num);
     System.out.println(end - start + "ms");

    }

    static void f(char[] arr, int n) {
    if (n % 2 == 1) {
    // 奇數直接返回
    return;
    }

    // 用來記錄左括號的個數
     int left = 0;
     int n2 = n >> 1;
    
     // 循環從1~n
     for (int i = 1; i <= n; i++) {
         if (arr[i] == '?') {
             // 如果問好,則多是左括號也多是右括號
             // 肯定右括號的最大上限
             int rightMax = i >> 1;
             if (i != n) {
                 /*
                     當dp到下標i時,能夠肯定的最多的右括號爲i/2,
                     若如今肯定的是左括號則前面i-1個格子就是肯定rightMax個右括號的結果
                     若如今肯定的是右括號,則前面i-1個格子肯定的就是rightMax-1個右括號的結果
                  */
                 // 由於只是肯定了右括號的最大上限,而不肯定究竟是多少,因此有不少種可能
                 // 每次後面多來一個右括號的話,前面的可能性就越多
                 for (; rightMax >= 1; rightMax--) {
                     dp[rightMax] += dp[rightMax - 1];
                     dp[rightMax] %= mod;
                 }
             } else {
                 //i==n必定是右括號,只要肯定前n-1個格子所肯定出來的n>>1-1個右括號的結果便可
                 dp[rightMax] = dp[rightMax - 1];
                 dp[rightMax] %= mod;
             }
         } else {
             // 若不是問好,則必定是左括號
             left++;
         }
     }
    
     if (left > n2) {
         // 左括號的個數超過一半
         return;
     }
    
     for (int i = 0; i < n2 - left; i++) {
         dp[n2] *= 25;
         dp[n2] %= mod;
     }
     num = dp[n2] % mod;

    } }

相關文章
相關標籤/搜索