算法练习---动态规划一(合唱团)

题目描述
有 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个,在一定条件下,求最大值
这道题很明显是求最优解,那么回顾我们的学的算法,用动态规划比较合适

动态规划可以求出所有解
所以我们可以这样想
当我们选第k个的时候,只要前k-1个是最大的,那么比较能参选第k个的所有元素和前k-1个的乘积,就能得到最大
但是我们注意到,学生的能力值有正有负,所以我们需要维护两个数组,分别保存最大值和最小值

class student{
    public long ability;
    public student(long ability){
        this.ability = ability;
    }
}

public class 合唱团 {


    public static long MAX_VALUE = Long.MAX_VALUE;
    public static long MIN_VALUE = Long.MIN_VALUE;

    public static void main(String[] args) {

        long Max = getMax();
        System.out.println(Max);

    }


    public static long getMax(){
        List<student> lits = new ArrayList<>();

        Scanner in = new Scanner(System.in);

        int n = in.nextInt();
        long abil;

        //输入数据
        for(int i = 0;i<n;i++){
            abil = in.nextLong();
            student s = new student(abil);
            lits.add(s);

        }

        int k = in.nextInt();
        int d = in.nextInt();

        //维护的两个最大值和最小值的数组,k表示选择几个学生,n表示第k个选取的学生
        //所以fMax[k][n]的乘积就是选择k个学生且第k个学生是n的时候的最大值
        long [][] fMax = new long[k][n];
        long [][] fMin = new long[k][n];
        long Max = MIN_VALUE;


        for(int i = 0;i<k;i++){
            for(int j =0;j<n;j++){
                fMax[i][j] = MIN_VALUE;
                fMin[i][j] = MAX_VALUE;
            }
        }


        //初始化选取一个的时候的最大最小值
        for(int i = 0 ;i<n;i++){
            fMax[0][i] = lits.get(i).ability;
            fMin[0][i] = lits.get(i).ability;
        }

        for(int i = 1 ;i<k;i++){

            for(int j = i;j<n;j++){
                long max = MIN_VALUE;
                long min = MAX_VALUE;
                long temp;
                //在j的d范围内可以选取的前i-1个的最大值
                //因为乘法交换律,所以每次只考虑当前元素的左侧有效范围
                //Math.max(i-1, j-d)的意思是如果左侧的元素个数大于等于d,那么可以定位到,如果小于,就从i-1开始,至少左侧有i-1个
                for(int t = Math.max(i-1, j-d);t<j;t++){
                    temp = Math.max(fMax[i-1][t]*lits.get(j).ability, fMin[i-1][t]*lits.get(j).ability);
                    if(max<temp){
                        max = temp;
                    }
                    temp = Math.min(fMax[i-1][t]*lits.get(j).ability, fMin[i-1][t]*lits.get(j).ability);

                    if(min>temp){
                        min=temp;
                    }
                }

                fMax[i][j] = max;
                fMin[i][j] = min;

            }
        }



        for(int i = k-1 ;i<n;i++){

            if(Max<fMax[k-1][i]){
                Max = fMax[k-1][i];
            }

        }


        return Max;

        }



}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值