动态规划之合唱团

问题:有n个学生站成一排,每个学生有一个能力值,从这n个学生中按照顺序选取k名学生,要求相邻两个学生的位置编号的差不超过d,使得这k个学生的能力值的乘积最大,求返回的最大乘积。

输入描述:

每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。

输出描述:

输出一行表示最大的乘积。

问题分析:
从n个学生中选出k名学生,求得乘积最大。典型的01背包问题。
记第k个人的位置为one,则可以用 memo[one][k] 表示从n个人中选择k个的方案。one可以是[k..n] 。
它的子问题是从n-1个学生中选出k-1个学生,可以用memo[left][k-1]表示这个方案
left >= k-1 left >= one-d(因为和one的编号相差不超过d) left <= one-1

因此 memo[one][k] = Math.max(memo[left][k-1]) 其中 left 选择为上面的范围

import java.util.*;
public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        int[] arr = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            arr[i] = sc.nextInt();   //人直接对应坐标
        }

        int kk = sc.nextInt();
        int dd = sc.nextInt();
        long[][] max = new long[n + 1][kk + 1];
        long[][] min = new long[n + 1][kk + 1];
        //初始化 当k=1 的情况
        for (int one = 1; one <= n; one++) {    //[1..n]个人中选1个且第n个一定被选中的情况
            max[one][1] = arr[one];
            min[one][1] = arr[one];
        }

        for (int k = 2; k <= kk; k++) { //从选择[2..kk]向上递推
            for (int one = k; one <= n; one++) {   //选定了[one][k] 即从[1..one]中选择k个人
                //由于有正负之分,因此设置最大值最小值
                long tempMax = Long.MIN_VALUE;
                long tempMin = Long.MAX_VALUE;
                /**
                 * 这里这个循环是 选择子问题的最优解 left >= k-1  left又>= one-dd 因此用max
                  */
                for (int left = Math.max(k - 1, one - dd); left <= one - 1; left++) {
                    if (tempMax < Math.max(max[left][k - 1] * arr[one], min[left][k - 1] * arr[one])) {
                        tempMax = Math.max(max[left][k - 1] * arr[one], min[left][k - 1] * arr[one]);
                    }
                    if (tempMin > Math.min(max[left][k - 1] * arr[one], min[left][k - 1] * arr[one])) {
                        tempMin = Math.min(max[left][k - 1] * arr[one], min[left][k - 1] * arr[one]);
                    }
                }
                max[one][k] = tempMax;
                min[one][k] = tempMin;
            }
        }
        long result = Long.MIN_VALUE;
        //最后得到 选择 k个人时候的所有解,选择其中最大的
        for(int one = kk; one <= n; one++){
            result = Math.max(result, max[one][kk]);
        }
        System.out.println(result);
    }
}

猜你喜欢

转载自blog.csdn.net/mooneal/article/details/80566800