Problem
Prof Shekhu has a matrix of N rows and M columns where rows are numbered from 0 to N-1 from top to bottom, and columns are numbered from 0 to M-1 from left to right. Each cell in the matrix contains a positive integer.
He wants to cut this matrix into N * M submatrices (each of size 1 * 1) by making horizontal and vertical cuts. A cut can be made only on the boundary between two rows or two columns.
Prof Shekhu invites his best student Akki for this job and makes an interesting proposition. Every time Akki makes a cut in a submatrix, before he makes the cut, he is awarded a number of coins equal to the minimum value in that submatrix. Note that with every cut, the total number of submatrices increases. Also, cuts in any two different submatrices are independent and likewise, Akki is awarded independently for the cuts in different submatrices.
Now, Akki has various ways in which he can make the cuts. Can you help him by maximizing the total number of coins he can gain?
Input
The first line of the input contains an integer T, the number of test cases. T test cases follow. The first line of each test case contains two integers N and M, as described above.
Next, there are N lines of M positive integers each; these describe the matrix.
Output
For each test case, output one line containing Case #x: y
, where x
is the test case number (starting from 1) and y
is the maximum possible number of coins that Akki can be awarded, if he makes the cuts in optimal order.
Limits
1 ≤ T ≤ 100.
1 ≤ each value in the matrix ≤ 105.
Small dataset
N = 1.
1 ≤ M ≤ 10.
Large dataset
1 ≤ N ≤ 40.
1 ≤ M ≤ 40.
Sample
In Sample Case #1, there are two possible ways in which Akki can make the cuts.
- Suppose that Akki first cuts the matrix horizontally. He is awarded the minimum value in the matrix: 1. Then he has to make vertical cuts in the two submatrices ([1, 2] and [3, 4]), for which he gets 1 and 3 coins, respectively.
- Suppose that Akki first cuts the matrix vertically. He is awarded the minimum value in the matrix: 1. Then he has to make horizontal cuts in the two submatrices (which have the transposes [1, 3] and [2, 4]), for which he gets 1 and 2 coins, respectively.
In Sample Case #2, Akki can be awarded at most 7 coins. One of the optimal ways is to first make the only horizontal cut to earn 1 coin. Then, in the upper submatrix [1, 2, 1], Akki can first make the cut immediately to the right of first column and then the cut immediately to the right of second column to earn a total of 2 coins. Similarly, in the lower submatrix [2, 3, 2], Akki can first make the cut immediately to the right of second column and then the cut immediately to the right of first column to earn a total of 4 coins.
In Sample Case #3, there is only one cut to be made.
题意:给一个N*M的矩阵,要切成N*M个小矩阵,每切一个矩阵就把当前矩阵的最小值取出来加到硬币数里面去。问所有切法里面获得硬币数的最大值。(只能沿着格子边缘横着切或者竖着切)
思路:记忆化搜索,memo[a][b][c][d]表示左上角坐标是(a,b),右下角坐标是(c,d)的矩阵被切成小矩形后,所能得到的最大值。
所以这是一道DP题,代码如下:
public long solve(int rowNum,int columnNum,int[][] matrix) {
long[][][][] memo=new long[rowNum][columnNum][rowNum][columnNum];
return helper(matrix,memo, 0, 0, rowNum-1, columnNum-1);
}
public long helper(int[][] matrix,long[][][][] memo,int topleftRow,int topLeftColumn,int rightBottomRow,int rightBottomColumn){
if(memo[topleftRow][topLeftColumn][rightBottomRow][rightBottomColumn]>0){
return memo[topleftRow][topLeftColumn][rightBottomRow][rightBottomColumn];
}
if(topleftRow==rightBottomRow&&topLeftColumn==rightBottomColumn){
return 0;
}
long maxVal=0;
//从cutRow下方切
for(int cutRow=topleftRow;cutRow<=rightBottomRow-1;cutRow++){
maxVal=Math.max(maxVal, helper(matrix,memo, topleftRow, topLeftColumn, cutRow, rightBottomColumn)+
helper(matrix,memo, cutRow+1, topLeftColumn, rightBottomRow, rightBottomColumn));
}
//从cutColumn右侧切
for(int cutColumn=topLeftColumn;cutColumn<=rightBottomColumn-1;cutColumn++){
maxVal=Math.max(maxVal, helper(matrix,memo, topleftRow, topLeftColumn, rightBottomRow, cutColumn)+
helper(matrix,memo, topleftRow, cutColumn+1, rightBottomRow, rightBottomColumn));
}
memo[topleftRow][topLeftColumn][rightBottomRow][rightBottomColumn]=maxVal+getMinValue(matrix, topleftRow, topLeftColumn, rightBottomRow, rightBottomColumn);
return memo[topleftRow][topLeftColumn][rightBottomRow][rightBottomColumn];
}
public long getMinValue(int[][] matrix,int topleftRow,int topLeftColumn,int rightBottomRow,int rightBottomColumn){
long min=Long.MAX_VALUE;
for(int i=topleftRow;i<=rightBottomRow;i++){
for(int j=topLeftColumn;j<=rightBottomColumn;j++){
min=Math.min(min, matrix[i][j]);
}
}
return min;
}
这道题google给出的官方思路如下:
Small dataset
在小数据集中,矩阵都是1-D 维的数组,即行数都是1。当数组大小为 M 时,需要用垂直的切来将其分为 M 部分,因此只要抉择“垂直切”的顺序就好了。
由于在这个数据集中 M 不能大于10,我们可以简单地考虑切割的所有可能的顺序。 对于每个排列,我们可以模拟采用这个排列的顺序来切割,并计算最终硬币数,然后输出我们在所有排列中找到的最大硬币数。 这种方法的复杂性是 O(M!),这对于小数据集是足够的。
但是,如果我们想出一个更好的方法,那么将在大数据集上也能应用。 一个有用的发现是,一旦进行了垂直切割,问题就被归结为两个相同类型的独立问题:一个用于在切线左侧的子数组,另一个用于在切线右侧的子数组。 这强烈地暗示了一个基于动态规划(DP)的解决方案。
我们可以将DP中的每个子问题定义为 f(L,R):从位置L到R的子数组的结果。最终我们需要的结果是 f(0,M-1)。 如下面的伪代码:
f(L, R, A): // A is the original array
ans = 0
// Assuming first cut is made immediately to the right of cut_position
for cut_position in L to R - 1, inclusive
ans = max(ans , f(L, cut_position) + f(cut_position + 1, R))
// we can calculate this in O(M).
current_coins = minimum value in A over positions L to R, inclusive
// For the current cut, we get the same number of coins no matter where we cut.
return ans + current_coins
Large dataset
在大数据集中,我们有一个二维矩阵,需要在其中进行水平切割和垂直切割。 因为切割的总数会高达80 (根据题中的限制,40+40),而这些切割会以任意的顺序进行,所以暴力排列方法不再起作用。
但是,DP方法可以解决这个问题。之前讨论的DP是一维的,现在可以被扩展为二维的,只要通过重新定义DP状态,使得DP数组描述的是一个子矩阵的答案,而不是一个子数组。因此,可以将 f(L,R,P,Q) 定义为 行L到R,列P到Q 的子矩阵的答案。 递归关系可以通过“迭代所有可能的水平/垂直切割”来获得。伪代码如下:
f(L, R, P, Q, A): // A is original matrix
ans = 0
// horizontal cuts
for horz_cut = L to R - 1, inclusive
ans = max(ans, f(L, horz_cut, P, Q) + f(horz_cut + 1, R, P, Q) + current_coins)
// vertical cuts
for vert_cut = P to Q - 1, inclusive
ans = max(ans, f(L, R, P, vert_cut) + f(L, R, vert_cut + 1, Q) + current_coins)
// we need to calculate this in less than O(N + M), if we don't want this step
// to dominate the transition cost
current_coins = minimum value in current submatrix (defined by L, R, P, Q)
// For the current cut, we get the same number of coins no matter where we cut.
return ans + current_coins
DP方法的时间复杂度是 O(N2M2(N + M)).
此外,特别感谢:https://www.cnblogs.com/zhixingr/p/7851708.html给了我思路。
这道题的地址:https://codejam.withgoogle.com/codejam/contest/dashboard?c=3254486#s=a&a=2