[LintCode] Stone Game

There is a stone game.At the beginning of the game the player picks n piles of stones in a line.

The goal is to merge the stones in one pile observing the following rules:

  1. At each step of the game,the player can merge two adjacent piles to a new pile.
  2. The score is the number of stones in the new pile.

You are to determine the minimum of the total score.

Example

For [4, 1, 1, 4], in the best solution, the total score is 18:

1. Merge second and third piles => [4, 2, 4], score +2
2. Merge the first two piles => [6, 4],score +6
3. Merge the last two piles => [10], score +10

Other two examples:
[1, 1, 1, 1] return 8
[4, 4, 5, 9] return 43

From the given example[4, 1, 1, 4] it looks like a greedy alogrithm of always picking two piles with smaller values

should solve this problem. However, as most of the time that an obvious greedy alogrithm is incorrect, this time is

no exception.

Counter example: [6, 4, 4, 6]

If we pick 4 + 4, score = 8, [6, 8, 6]

then we pick 6 + 8, score = 22, [14, 6]

finally we pick 14 + 6, score = 42.

If we pick 6 + 4, score = 10, [10, 4, 6]

then we pick 4 + 6 = 10, score = 20, [10, 10]

finally we pick 10 + 10, score = 40.

First thought: Recursion

for all stones A[0]....A[n - 1], we can solve this problem using the following formula.

f(0, n - 1) = min{f(0, k) + f(k + 1, n - 1) + sum[0... n - 1]}, for all k: [0, n - 2]

Using this formula, we can recursively solve all subproblems.

Alert:  do we have the overlapping subproblems issue?

Yes, we do have.

For example: to solve f(0, 4), we have to solve f(0, 0), f(1, 4), f(0, 1), f(2, 4), f(0, 2), f(3, 4), f(0, 3), f(4, 4)

         to solve f(0, 3), we have to solve f(0, 0), f(1, 3), f(0, 1), f(2, 3), f(0, 2), f(3, 3)

The subproblems in red are redundantly computed.

So there is the all-mighty dynamic programming solution.

State:

dp[i][j] represents the min cost of merging A[i...j];

Function:

dp[i][j] = min{dp[i][k] + dp[k + 1][j] + prefixSum[j + 1] - prefixSum[i]} for all k: [i, j - 1]

Initialization:

dp[i][i] = 0;

dp[i][j] = Integer.MAX_VALUE for all j > i;

prefixSum[i]: the sum of the first ith elements of A;

Answer:

dp[0][A.length - 1];

This problem is different with typical dp problems in the for-loop.

To correctly solve this problem, we must solve all subproblems of smaller length.

As a result, instead of the typical for loop as shown in the following incorrect

solution, we need to do a for loop with subproblem‘s length and its start index.

For example, in the incorrect solution, to solve dp[0][3], we need to solve

dp[0][0], dp[1][3], dp[0][1], dp[2][3], dp[0][2], dp[3][3].

But when trying to solve dp[0][3], i == 0, dp[1][3] and dp[2][3] have not been

solved yet!!

But if we first solve smaller subproblems of length 2 and 3, we can then solve

dp[0][3] of length 4 correctly.

length 2: dp[0][1]  dp[2][3]

length 3: dp[1][3]  dp[0][2]

Incorrect bottom up dp algorithm

 1 public class Solution {
 2     public int stoneGame(int[] A) {
 3         if(A == null || A.length <= 1){
 4             return 0;
 5         }
 6         int[] prefixSum = new int[A.length + 1];
 7         for(int i = 1; i <= A.length; i++){
 8             prefixSum[i] = prefixSum[i - 1] + A[i - 1];
 9         }
10         int[][] dp = new int[A.length][A.length];
11         for(int i = 0; i < A.length - 1; i++){
12             for(int j = i + 1; j < A.length; j++){
13                 dp[i][j] = Integer.MAX_VALUE;
14             }
15         }
16         for(int i = 0; i < A.length; i++){
17             dp[i][i] = 0;
18         }
19         for(int i = 0; i < A.length - 1; i++){
20             for(int j = i + 1; j < A.length; j++){
21                 for(int k = i; k < j; k++){
22                     dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j] + prefixSum[j + 1] - prefixSum[i]);
23                 }
24             }
25         }
26         return dp[0][A.length - 1];
27     }
28 }

Correct bottom up dp solution

 1 public class Solution {
 2     public int stoneGame(int[] A) {
 3         if(A == null || A.length <= 1){
 4             return 0;
 5         }
 6         int[] prefixSum = new int[A.length + 1];
 7         for(int i = 1; i <= A.length; i++){
 8             prefixSum[i] = prefixSum[i - 1] + A[i - 1];
 9         }
10         int[][] dp = new int[A.length][A.length];
11         for(int i = 0; i < A.length - 1; i++){
12             for(int j = i + 1; j < A.length; j++){
13                 dp[i][j] = Integer.MAX_VALUE;
14             }
15         }
16         for(int i = 0; i < A.length; i++){
17             dp[i][i] = 0;
18         }
19         for(int len = 2; len <= A.length; len++){
20             for(int start = 0; start + len - 1 < A.length; start++){
21                 int end = start + len - 1;
22                 for(int k = start; k < end; k++){
23                     dp[start][end] = Math.min(dp[start][end],
24                                      dp[start][k] + dp[k + 1][end] + prefixSum[end + 1] - prefixSum[start]);
25                 }
26             }
27         }
28         return dp[0][A.length - 1];
29     }
30 }
时间: 2025-01-02 16:51:45

[LintCode] Stone Game的相关文章

[LeetCode] 877. Stone Game == [LintCode] 396. Coins in a Line 3_hard tag: 区间Dynamic Programming, 博弈

Alex and Lee play a game with piles of stones.  There are an even number of piles arranged in a row, and each pile has a positive integer number of stones piles[i]. The objective of the game is to end with the most stones.  The total number of stones

POJ 1740 A New Stone Game

A New Stone Game Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 5453   Accepted: 2989 Description Alice and Bob decide to play a new stone game.At the beginning of the game they pick n(1<=n<=10) piles of stones in a line. Alice and Bob

[lintcode the-smallest-difference]最小差(python)

题目链接:http://www.lintcode.com/zh-cn/problem/the-smallest-difference/ 给定两个整数数组(第一个是数组 A,第二个是数组 B),在数组 A 中取 A[i],数组 B 中取 B[j],A[i] 和 B[j]两者的差越小越好(|A[i] - B[j]|).返回最小差. 排好序后用两个指针分别扫描两个数组,每次更新他们的差值的绝对值.并且依据他们两个数字的大小来决定谁来移动指针. 1 class Solution: 2 # @param

Lifting the Stone(hdoj1115)

Lifting the Stone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 6104    Accepted Submission(s): 2546 Problem Description There are many secret openings in the floor which are covered by a big

lintcode.44 最小子数组

最小子数组 描述 笔记 数据 评测 给定一个整数数组,找到一个具有最小和的子数组.返回其最小和. 注意事项 子数组最少包含一个数字 您在真实的面试中是否遇到过这个题? Yes 哪家公司问你的这个题? Airbnb Amazon LinkedIn Cryptic Studios Dropbox Apple Epic Systems TinyCo Yelp Hedvig Zenefits Uber Snapchat Yahoo Microsoft Bloomberg Facebook Google

lintcode 66.67.68 二叉树遍历(前序、中序、后序)

AC代码: /** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode(int val) { * this.val = val; * this.left = this.right = null; * } * } */ public class Solution { /** * @param root: The r

[LintCode/LeetCode]——两数和、三数和、四数和

LintCode有大部分题目来自LeetCode,但LeetCode比较卡,下面以LintCode为平台,简单介绍我AC的几个题目,并由此引出一些算法基础. 1)两数之和(two-sum) 题目编号:56,链接:http://www.lintcode.com/zh-cn/problem/two-sum/ 题目描述: 给一个整数数组,找到两个数使得他们的和等于一个给定的数 target. 你需要实现的函数twoSum需要返回这两个数的下标, 并且第一个下标小于第二个下标.注意这里下标的范围是 1

Lintcode 469. 等价二叉树

----------------------------------------------- AC代码: /** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode(int val) { * this.val = val; * this.left = this.right = null; * } * } */

Lintcode 75.寻找峰值

--------------------------------------- 按照给定的峰值定义,峰值的左半部分一定是递增的,所以只要找到不递增的即可. AC代码: class Solution { /** * @param A: An integers array. * @return: return any of peek positions. */ public int findPeak(int[] A) { for(int i=1;i<A.length;i++){ if(A[i]>=