【Java题解】洛谷题目P3205合唱队-区间动态规划解法

题目描述:

[HNOI2010]合唱队 - 洛谷https://www.luogu.com.cn/problem/P3205

题目信息提取:

         这个题目要求我们按照某种顺序将这个队伍中的所有人按照从左到右的顺序去排队,其实题目中的很多无用信息可以忽略,总结为,寻找某种数列,将这个数列按照从左到右的顺序依次进行排列,排列规则是,如果当前数字大于前一个已经排列完的数,那么他就放在新的当前排列的数列的最后面,如果他小于前一个已经排列玩的数字,那么他就放在当前排列的数列的最前面,看下面一个例子:

例如 初始数列为 33 22 11 44 那么他的排列顺序就是

因为22<33 所以 22 33

因为11<22 所以 11 22 33

因为44>11 所以 11 22 33 44

题目思路归纳:

       我们了解了基本的规则以后,看题目,题目给出一个期望得到的排列后的数列,让我们求出有几种默认的数列排放顺序可以经过排序后得到期望数列。所以我们可以考虑假如 我们要求的 期望数列为 11 22 33 44既然是动态规划,我们将应该先去总结出有无状态之间的转换关系,即状态转移方程,11 22 33 44的初始队列数量如何从一个子结构转化出来,11 22 33 44 他既可能是 22 33 44 加入了11也可能是11 22 33 加入了44,只有这两种可能,因为最新加入的数字一定在队列的最左侧或者是最右侧这句话是重点所以标红,所以我们知道了状态转移的规则,即当前状态一定是从他的左侧不包含最左侧数字的最优子结果和右侧不包含右侧数字的最优子结果转化而来的,这句话的理解非常重要,但是他是如何转化的呢?

        还是看11 22 33 44 这个例子假如 11 22 33 44 是从11 22 33转化来的,那么数列最右侧的数字一定大于他的前一个数字,否则不成立,即不可能转化,那么他的前一个数字会在哪呢,其实同理就知道,他的前一个数字肯定就是11,33其中的某一个,这两种情况都可能如果是11先排序然后44排序,可以看到情况符合条件是成立的,例如是33排序后44排序,情况也是成立的,所以这两种情况都可以排列出我们要求的顺序,所以这两种情况相加就是44从右侧加进来的情况的数量,那么还有一种情况就是从22 33 44转化来的,即11是最后排序的,那么我们再来讨论11,11也有两种情况,即22和44都有可能是在11之前加入的,假如11小于22,那么22是最后加入的情况也是成立的,假如11小于 44,那么11最后加入的情况也是成立的,我们可以看到当前的数列也是满足这个要求的,所以同理11最后加入的情况的数量就等于22加入的数量加上44加入的数量之和,我们讨论完了这两种情况,不存在其他的情况了,所以11 22 33 44 的所有初始队列种类就是两者之和:我们有了思路就可以总结转台转移方程了,

这里我们使用两个数组来表示left[i][j]表示在下标i-j的这个数列中,最后加入的是i的种类数目而right[i][j]反之.

状态转移方程:

left[i][j] += left[i+1][j] when num[i] < num[i+1] eg:num是期望数列

left[i][j] += right[i+1][j] when num[i]<num[j] 

right[i][j] += right[i][j-1] when num[j]>num[j-1]

right[i][j] += left[i][j-1] when num[j]>num[i] 

代码书写:

        我们知道了状态转移方程也就基本知道了代码结构了,但是还有一些细节需要注意,例如状态的初态和循环测顺序,首先我门看循环顺序,我们要确定循环的顺序首先看状态转移方程中状态之间的关系,在第一个和第二个状态转移方程中我们都需要i+1时的状态,所以我们可以确定i需要逆序递减求值,在2,3中可以看到都需要j-1的状态所以可以确定j需要递增,所以我们可以确定i的循环要从n开始逐渐递减,j要从i+1开始逐渐递增,来不断的扩展子状态的范围,然后我们需要确认初态,我们要给left[i][j]或者是right[i][j]其中的任何一个赋值1以确保任意值不会为0,只能赋值其中一个,因为当只有一个数时,它只能是从左边进或者是右边进,互相矛盾,所以最终的代码为:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine());
        String[] intStr = reader.readLine().split(" ");
        int[] ans = new int[n];
        for(int i = 0;i<n;i++){
            ans[i] = Integer.parseInt(intStr[i]);
        }
        int[][] left = new int[n][n];
        int[][] right = new int[n][n];
        for(int i = 0;i<n;i++){
            left[i][i] = 1;
        }
        for(int i = n-1;i>=0;i--){
            for(int j = i+1;j<n;j++){
                if(ans[i]<ans[j])
                    left[i][j] +=right[i+1][j];
                if(ans[i]<ans[i+1])
                    left[i][j] +=left[i+1][j];
                if(ans[j]>ans[j-1]){
                    right[i][j]+=right[i][j-1];
                }
                if(ans[j]>ans[i])
                    right[i][j] +=left[i][j-1];
                right[i][j]%=19650827;
                left[i][j]%=19650827;
            }
        }
        System.out.println((left[0][n-1]+right[0][n-1])%19650827);
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷p5736 【深基7.例2】质数筛是一个关于质数筛法的题目,要求我们根据输入的一个正整数n,找出小于等于n的所有质数。 质数是指只能被1和自身整除的大于1的整数,比如2、3、5、7等。质数筛法是一种常见且高效的找出质数的方法。 在这道题中,我们需要使用质数筛法来找出小于等于n的所有质数。首先,我们定义一个boolean类型的数组isPrime,用来标记每个数字是否是质数。初始时,我们将isPrime数组的所有元素都设置为true。 然后,我们从2开始遍历到n,对于每个数字i,如果isPrime[i]为true,说明这个数字是质数。那么我们就需要将i的倍数都标记为false,因为这些倍数一定不是质数。具体做法是,从2*i开始,每次增加i,将对应的isPrime数组的元素都置为false。 遍历结束后,isPrime数组中为true的元素即为小于等于n的所有质数。我们可以遍历isPrime数组,将为true的下标即为质数输出即可。 这个算法的时间复杂度是O(nloglogn),相较于直接遍历每个数字并判断是否是质数的方法,时间复杂度更低,效率更高。 对于这个题目java实现,我们可以使用一个boolean数组isPrime来标记每个数字是否是质数,使用一个ArrayList来存储所有的质数,最后将ArrayList转化为数组输出。 代码示例如下: ``` import java.util.ArrayList; public class Main{ public static void main(String[] args){ int n = 100; // 输入的正整数n boolean[] isPrime = new boolean[n+1]; // 标记每个数字是否是质数的数组 ArrayList<Integer> primes = new ArrayList<>(); // 存储质数的ArrayList // 初始化isPrime数组 for(int i=2; i<=n; i++){ isPrime[i] = true; } // 质数筛法 for(int i=2; i<=n; i++){ if(isPrime[i]){ primes.add(i); for(int j=2*i; j<=n; j+=i){ isPrime[j] = false; } } } // 将ArrayList转化为数组输出 int[] result = new int[primes.size()]; for(int i=0; i<primes.size(); i++){ result[i] = primes.get(i); } // 输出结果 for(int i=0; i<result.length; i++){ System.out.print(result[i] + " "); } } } ``` 这样,我们就可以通过这段代码来实现洛谷p5736题目的要求,找出小于等于输入的正整数n的所有质数,并将它们按从小到大的顺序输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值