算法设计与分析: 3-6 石子合并问题(圆排列)

本文探讨了石子合并问题在圆排列情况下的解决方案,通过转化成直线排列问题,利用动态规划计算合并石子的最小和最大得分。通过分析2n-1堆石子的直线排列,找到圆排列的最优值。提供了问题描述、算法分析及Java实现的相关内容。

3-6 石子合并问题(圆排列)


问题描述

在一个圆形操场的四周摆放着n 堆石子。现要将石子有次序地合并成一堆。
规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。


分析

圆排列的石子合并问题可以转化为n个直线排列的石子合并问题
圆排列:A1,A2,...,AnA1,A2,...,An
设计扩充的直线排列:A1,A2,...,An,A1,A2,...,An1A1,A2,...,An,A1,A2,...,An−1
上面设计的扩充直线排列,可以表示圆排列的n种情形。

设扩充的n-1个元素为An+iAn+i(1<=i<=n-1),则An+iAn+i=AiAi(1<=i<=n-1)。这样就将n堆石子的圆排列合并问题转化为一个2n-1堆石子的直线排列合并问题。

设m[i, j], 1<=i<=j<=2n-1 为合并Ai...jAi...j的最优值,则所述圆排列中n种情形的最优值分别为:m[1, n], m[2, n+1], … , m[n, 2n-1]。
min(或max){m[i, n+i-1]} (1<=i<=n)即为所求圆排列石子合并问题的最优值。


Java

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

public class ShiZiHebing {
    private static int n;
    private static int[][] m;
    private static int[][] s;
    private static int[] a;
    private static int[] backup;
    private static int minScore;
    private static int maxScore;


    public static void main(String[] args){
        Scanner input = new Scanner(System.in);

        while (true){
            n = input.nextInt();
            a = new int[2*n];
            m = new int[2*n][2*n];
            s = new int[2*n][2*n];
            for(int i=1; i<=n; i++)
                a[i] = input.nextInt();

            //a会改变,备份一下
            backup = Arrays.copyOf(a, 2*n);

            minScore = circleMerge(a,0);//type为0,最小得分

            //还原
            m = new int[2*n][2*n];
//            s = new int[2*n][2*n];
            maxScore = circleMerge(backup, 1);//type为1,最大得分

            System.out.println(minScore);
            System.out.println(maxScore);
        }
    }

    //type=0: 最小得分; type=1: 最大得分
    private static int circleMerge(int[] a, int type){
        for(int i=1; i<=n-1; i++)
            a[n+i] = a[i];

        n = 2*n-1;
        if(type == 0)
            minSum(a);
        else
            maxSum(a);

        n = (n+1)/2;
        int mm = m[1][n];
        for(int i=2; i<=n; i++)//圆排列 转化为 直线排列后,要遍历一下
            if((type==0) && (m[i][n+i-1]<mm) || (type==1) && (m[i][n+i-1]>mm))
                mm = m[i][n+i-1];

        return mm;
    }

    private static void minSum(int[] a){
        for(int i=2; i<=n; i++)
            a[i] = a[i] + a[i-1];

        for(int r=2; r<=n; r++)
            for(int i=1; i<=n-r+1; i++){
                int j = i+r-1;
                int i1 = i+1;
                int j1 = j;
                if(s[i][j-1] > i)
                    i1 = s[i][j-1];
                if(s[i+1][j] > i)
                    j1 = s[i+1][j];
                m[i][j] = m[i][i1-1] + m[i1][j];
                s[i][j] = i1;
                for(int k=j1; k>=i1+1; k--){
                    int q = m[i][k-1] + m[k][j];
                    if(q < m[i][j]){
                        m[i][j] = q;
                        s[i][j] = k;//记录合并的断开位置k
                    }
                }

                m[i][j] = m[i][j] + a[j] -a[i-1];
            }
    }

    private static void maxSum(int[] a){
        for(int i=2; i<=n; i++)
            a[i] = a[i] + a[i-1];

        for(int r=2; r<=n; r++)
            for(int i=1; i<=n-r+1; i++){
                int j = i+r-1;
                if(m[i+1][j] > m[i][j-1])
                    m[i][j] = m[i+1][j] + a[j] - a[i-1];
                else
                    m[i][j] = m[i][j-1] + a[j] - a[i-1];
            }
    }
}

Input & Output

5
2 3 3 5 2
34
47

4
4 4 5 9
43
54

Reference

王晓东《计算机算法设计与分析》(第3版)P90

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值