锯木棒问题 Oj问题

写在前面:成功AC一道题总是多舛啊

题目描述

/**
* 题目描述
* xiaok大佬最近再雇佣工人给他掰木棒。把一根长为L的木棒锯成两段,他需要支付给工人L元钱。xiaok大佬一开始只有长为L的一根木棒,他想把它锯成n段,
* 每段长度分别为L1,L2,...,Ln,问xiaok大佬最少要付给工人多少钱?
*
* 输入
* 第一行两个整数n,L(1<n<103,n<L<109)
*
* 第二行n个整数L1,L2,...,Ln(0<Li<L,且保证L1+L2+...+Ln=L)
*
* 输出
* 输出一个整数,表示最小花费
* 样例输入
* 3 21
* 8 5 8
* 样例输出
* 34
*/

1. 经过思考,我判断这题类似与动态规划里面的经典问题矩阵连乘相似,就是每次找一个最优的划分点,时间复杂度O(n**2),
代码如下,这是错误答案!
public class Main {
    public static long getMinCost(int length, int seg, long [] segLens){
        int[][] dp = new int[seg][seg];
        for(int len = 2; len <= seg; ++len){ // 矩阵个数
            for(int row =  1; row <= seg; ++row){
                int col = row + len -1;
                if(col > seg)break;
                int minCost = Integer.MAX_VALUE;
                int sums = 0;
                for(int m = row; m <= col; ++m){
                    sums += segLens[m-1];
                }
                for(int k = row; k < col; ++k){
                    minCost = Math.min(minCost, dp[row-1][k-1] + dp[k][col-1] + sums);
                }
                dp[row-1][col-1] = minCost;
            }
        }
        return dp[0][seg-1];
    }
}

 

2. 经过测试错误50%,好家伙,想了好久,最后问了同学,其实这个是有区别的,矩阵连乘划分的结果是有顺序。比如说上面的21 按照矩阵连乘的最优划分是8,5,8,但是这道题不同啊,我只要把木棍锯成三段不久可以了吗,也可以是8,8,5啊,这个测试数据有点特殊,最后的结果是一样的,也就是说按照矩阵连乘的结果不一定就是最小划分,可能还存在其他的顺序无关的切分方法。

所以我的方法是贪心,每次都先把最长的那个段给切出来,下一次切分的时候代价就最小了。

以下代码75%的通过率

public static long getMinCost(int length, int seg, long [] segLens){// 此题关键不能用矩阵连乘来做,还是自己思维不到位
        // 矩阵连乘划分出来的具有一定的顺序
        // 8, 5, 8 这个是矩阵连乘的顺序
        // 8, 8, 5这个也满足要求啊
        // 这个题的思路就是贪心啦,每次都切最大的一段,保证剩下的可以最小


        // 1. 升序排列,排序 O(nlgn)
        Arrays.sort(segLens);

        // 2. 倒序遍历
        // minCost是最小花费,seg是数组长度,segLens表示将要划分的长度
        long minCost = 0;
        for(int i = seg - 1; i > 0; --i){
             minCost +=  (long) length;
             length -= segLens[i];
        }
        return  minCost;
        // 75%正确率
}

3. 这肯定不行啊,还得优化,最后我采用自底向上的方法进行合并,用到了优先级队列,不过写的时候,我的有一种写法还是75%通过率,我还是不清楚,最后修改了一种解法

public class Main {
    public static long getMinCost(int length, int seg, long [] segLens){
// 思路三
// 1. 每次取最小的两个数合并
PriorityQueue<Long> priorityQueue = new PriorityQueue<>();
for(int i = 0; i < seg; ++i){
priorityQueue.add(segLens[i]);
}

long minCost = 0;
while (priorityQueue.size() > 1){
long sums = priorityQueue.remove() + priorityQueue.remove();
minCost += sums;
priorityQueue.add(sums);
// 错误代码
// 这两段的逻辑一样的啊!
// minCost += priorityQueue.remove() + priorityQueue.remove();
//priorityQueue.add(minCost);
}
// return priorityQueue.remove()
return minCost;
 }

 

4. 完整的ac代码,这道题还是暴露了自己严重的思维缺陷,总是喜欢跳进自己的思维陷阱,基础不扎实,还跳不出来,上课前还得预习下啊!!!

/**
 * @Author Fizz Pu
 * @Date 2020/10/18 下午5:02
 * @Version 1.0
 * 失之毫厘,缪之千里!
 */


import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;

/**
 * 题目描述
 * xiaok大佬最近再雇佣工人给他掰木棒。把一根长为L的木棒锯成两段,他需要支付给工人L元钱。xiaok大佬一开始只有长为L的一根木棒,他想把它锯成n段,
 * 每段长度分别为L1,L2,...,Ln,问xiaok大佬最少要付给工人多少钱?
 *
 * 输入
 * 第一行两个整数n,L(1<n<103,n<L<109)
 *
 * 第二行n个整数L1,L2,...,Ln(0<Li<L,且保证L1+L2+...+Ln=L)
 *
 * 输出
 * 输出一个整数,表示最小花费
 * 样例输入
 * 3 21
 * 8 5 8
 * 样例输出
 * 34
 */

// 这个题有个关键点就是Li为整数,每段长度为整数
// 从i处划分,划分成i段和n-i段,那么就可以递归的解决问题
// 这还是一个最优解的问题,左边如果出现最优解,右边出现左右解,那么总结果就是最优的
// 当然会出现重复计算问题
// 这道题和矩阵连乘思路很像
// dp[i][j]表示表示把长度为i的木板分成j段
// dp[i][j] = dp[i][k] + dp[k][j] + sum(i:j)


public class Main {
    public static long getMinCost(int length, int seg, long [] segLens){
        // 思路三
        // 1. 每次取最小的两个数合并
        PriorityQueue<Long> priorityQueue = new PriorityQueue<>();
        for(int i = 0; i < seg; ++i){
            priorityQueue.add(segLens[i]);
        }

        long minCost = 0;
        while (priorityQueue.size() > 1){
            long sums = priorityQueue.remove() + priorityQueue.remove();
            minCost +=  sums;
            priorityQueue.add(sums);
        }
        return minCost;
    }


    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int seg = scanner.nextInt(), len = scanner.nextInt();
        long[] segLens = new long[seg];
        for(int i = 0; i < seg; ++i){
            segLens[i] = scanner.nextLong();
        }
        System.out.println(getMinCost(len, seg, segLens));
    }
}

 

 



posted @ 2020-10-19 09:09  FizzPu  阅读(627)  评论(1编辑  收藏  举报