蓝桥杯——试题 算法训练 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;

    } }

相关文章
相关标签/搜索