鋸木棒問題 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));
    }
}

 

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM