1423. 可获得的最大点数

1423. 可获得的最大点数

题目

几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。

每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。

你的点数就是你拿到手中的所有卡牌的点数之和。

给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。
示例
输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。

思路

既然求两端的k个值的最大值,可以反过来思考,我们可以求中间的数组连续部分的最小值。问题就转换为,当窗口大小为n-k时,求滑动窗口中的最小值。

代码

    public static int maxScore(int[] cardPoints, int k) {
        int n = cardPoints.length;
        int S[] = new int[n];
        S[0] = cardPoints[0];
        //求前缀和
        for(int i=1;i<n;i++){
            S[i] = S[i-1]+cardPoints[i];
        }
        int m =  Integer.MAX_VALUE;
        if(n==k){
            return S[n-1];
        }
        for(int i=n-k-1;i<n;i++){
            if(i==n-k-1){
                m = S[i];
                continue;
            }
            // S[i]-S[i-(n-k)]表示从i-(n-k)+1到i之间数据的和
            m = m>(S[i]-S[i-(n-k)])?(S[i]-S[i-(n-k)]):m;
        }
        //最后,要返回S[n-1]-m,因为要还原成原问题
        return S[n-1]-m;
    }

补充

这题还有DP的解法,但是我的DP解法超时了,也没找到优化的方法,放一下思路吧
思路是这样的。
定义 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示在 c a r d P o i n t s [ i : j ] cardPoints[i:j] cardPoints[i:j]子数组中,选择k个元素,所能达到的最大值(这里i和j都是包含在数组内的,为了方便,f数组定义为n+1和n+1的)。

那么状态转移方程就是
f [ i ] [ j ] [ k + 1 ] = m a x { f [ i + 1 ] [ j ] [ k ] + A [ i ] , f [ i ] [ j − 1 ] [ k ] + A [ j ] } f[i][j][k+1] = max\{ f[i+1][j][k]+A[i], f[i][j-1][k]+A[j]\} f[i][j][k+1]=max{f[i+1][j][k]+A[i],f[i][j1][k]+A[j]}
当然,上面的方程是有限制的,比如 j > = i j>=i j>=i,如果 j − i + 1 < k , t h e n c o n t i n u e ; j-i+1<k, then \quad continue; ji+1<k,thencontinue;等等吧。代码如下:

public static int maxScore(int[] cardPoints, int k) {
        int n = cardPoints.length;
        int f[][][] = new int[n+1][n+1][k+1];
        int index  =1;
        while(index<=k){
            for(int i=1;i<=n;i++){
                for(int j=i;j<=n;j++){
                    if(j-i+1<index){
                        continue;
                    }
                    if(index==1){
                        if(i==j){
                            f[i][j][index] = cardPoints[i-1];
                        }else{
                            f[i][j][index] = (cardPoints[i-1])>(cardPoints[j-1])?(cardPoints[i-1]):(cardPoints[j-1]);
                        }
                    }else{
                        f[i][j][index]  = (f[i+1][j][index-1] +cardPoints[i-1])>(f[i][j-1][index-1] +cardPoints[j-1])?(f[i+1][j][index-1]  +cardPoints[i-1]):(f[i][j-1][index-1] +cardPoints[j-1]);
                    }
                }
            }
            index++;
        }
        return f[1][n][k];
    }

但是,这样的话,需要开一个三维数组,很浪费内存。可以直接用二维数据代替,只不过,第二层循环的遍历需要从尾到头,不再是从头到尾,代码如下:

    public static int maxScore(int[] cardPoints, int k) {

        int n = cardPoints.length;
        int f[][] = new int[n+1][n+1];
        int index  =1;
        while(index<=k){
            for(int i=1;i<=n;i++){
                for(int j=n;j>=i;j--){
                    if(j-i+1<index){
                        continue;
                    }
                    if(index==1){
                        if(i==j){
                            f[i][j] = cardPoints[i-1];
                        }else{
                            f[i][j] = (cardPoints[i-1])>(cardPoints[j-1])?(cardPoints[i-1]):(cardPoints[j-1]);
                        }

                    }else{
                        f[i][j] = (f[i+1][j]+cardPoints[i-1])>(f[i][j-1] +cardPoints[j-1])?(f[i+1][j] +cardPoints[i-1]):(f[i][j-1] +cardPoints[j-1]);
                    }

                }
            }
            index++;

        }
        return f[1][n];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值