【训练题17:二维区间DP】棋盘分割 | 洛谷 P1436

棋盘分割 | 洛谷 P1436

题外话

之前感觉这题是棋盘DP的一个切割版本。
睡了一觉,想到了:这不就是平面的区间DP吗?
然后百度了一下,真有二维区间DP的说法。

难度

提 高 + / 省 选 − \color{blue}提高+/省选- +/

题意

给一个 8 × 8 8\times8 8×8的棋盘,每个格子上有一个数字。
切割一次,在剩下的两个矩形中选择一个,继续切割一次……直到剩下 n n n 个矩形。
在这里插入图片描述
然后问你,这个剩下的每个矩形的平方和的最小值为多少。

数据范围

1 < n < 15 1<n<15 1<n<15

思路

  • 每次只能竖切或者横切,经典的棋盘切割题。
  • d p [ i ] [ j ] [ a ] [ b ] [ k ] dp[i][j][a][b][k] dp[i][j][a][b][k] 表示左上角(i,j)到右下角(a,b)的矩形切割成k个矩形的最小平方和
  • 考虑状态转移,我们可以横切也可以竖切。并且如果切完后整个矩形会分成 k k k个矩形,那么肯定要其中一个子矩形分成1份,另一个子矩形分成 k − 1 k-1 k1份。
  • 转移方程:
    h h h表示中间的切割断横。则:(画个图算一下点坐标即可)
    d p [ i ] [ j ] [ a ] [ b ] [ k ] = m i n ( d p [ i ] [ j ] [ a ] [ b ] [ k ] , m i n ( d p [ i ] [ j ] [ a ] [ h ] [ 1 ] + d p [ i ] [ h + 1 ] [ a ] [ b ] [ k − 1 ] , d p [ i ] [ j ] [ a ] [ h ] [ k − 1 ] + d p [ i ] [ h + 1 ] [ a ] [ b ] [ 1 ] ) ) ; d p [ i ] [ j ] [ a ] [ b ] [ k ] = m i n ( d p [ i ] [ j ] [ a ] [ b ] [ k ] , m i n ( d p [ i ] [ j ] [ h ] [ b ] [ 1 ] + d p [ h + 1 ] [ j ] [ a ] [ b ] [ k − 1 ] , d p [ i ] [ j ] [ h ] [ b ] [ k − 1 ] + d p [ h + 1 ] [ j ] [ a ] [ b ] [ 1 ] ) ) ; dp[i][j][a][b][k] = min(dp[i][j][a][b][k],min(dp[i][j][a][h][1] + dp[i][h+1][a][b][k-1],dp[i][j][a][h][k-1] + dp[i][h+1][a][b][1]));\\ dp[i][j][a][b][k] = min(dp[i][j][a][b][k],min(dp[i][j][h][b][1] + dp[h+1][j][a][b][k-1],dp[i][j][h][b][k-1] + dp[h+1][j][a][b][1])); dp[i][j][a][b][k]=min(dp[i][j][a][b][k],min(dp[i][j][a][h][1]+dp[i][h+1][a][b][k1],dp[i][j][a][h][k1]+dp[i][h+1][a][b][1]));dp[i][j][a][b][k]=min(dp[i][j][a][b][k],min(dp[i][j][h][b][1]+dp[h+1][j][a][b][k1],dp[i][j][h][b][k1]+dp[h+1][j][a][b][1]));

初始化与一点小优化:

  • 计算 d p [ i ] [ j ] [ a ] [ b ] [ 1 ] dp[i][j][a][b][1] dp[i][j][a][b][1] 就是这个子矩阵的每个元素的平方和。可以使用二位差分
  • 递推 d e [ i ] [ j ] = d e [ i − 1 ] [ j ] + d e [ i ] [ j − 1 ] − d e [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] de[i][j]=de[i-1][j]+de[i][j-1]-de[i-1][j-1]+a[i][j] de[i][j]=de[i1][j]+de[i][j1]de[i1][j1]+a[i][j]
  • 获取左上角(i,j),右下角(a,b)矩阵的和 d e [ a ] [ b ] − d e [ i − 1 ] [ b ] − d e [ a ] [ j − 1 ] + d e [ i − 1 ] [ j − 1 ] de[a][b]-de[i-1][b]-de[a][j-1]+de[i-1][j-1] de[a][b]de[i1][b]de[a][j1]+de[i1][j1]

核心代码

时间复杂度 O ( 8 5 × n ) O(8^5\times n) O(85×n)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 10;
const int INF = 0x3f3f3f3f;

int de[MAX][MAX];
int dp[MAX][MAX][MAX][MAX][20];

int main()
{
    
    
    int K;cin >> K;
    for(int i = 1;i <= 8;++i)
    for(int j = 1;j <= 8;++j){
    
    
        int t;cin >> t;
        de[i][j] = de[i-1][j] + de[i][j-1] - de[i-1][j-1] + t;
    }

    for(int i = 1;i <= 8;++i)
    for(int j = 1;j <= 8;++j)
    for(int a = i;a <= 8;++a)
    for(int b = j;b <= 8;++b){
    
    
        dp[i][j][a][b][1] = (de[a][b] - de[a][j-1] - de[i-1][b] + de[i-1][j-1]) * (de[a][b] - de[a][j-1] - de[i-1][b] + de[i-1][j-1]);
    }

    for(int k = 2;k <= K;++k)
    for(int i = 1;i <= 8;++i)
    for(int j = 1;j <= 8;++j)
    for(int a = i;a <= 8;++a)
    for(int b = j;b <= 8;++b){
    
    
        dp[i][j][a][b][k] = INF;
        for(int h = j;h < b;++h)
            dp[i][j][a][b][k] = min(dp[i][j][a][b][k],min(dp[i][j][a][h][1] + dp[i][h+1][a][b][k-1],dp[i][j][a][h][k-1] + dp[i][h+1][a][b][1]));
        for(int h = i;h < a;++h)
            dp[i][j][a][b][k] = min(dp[i][j][a][b][k],min(dp[i][j][h][b][1] + dp[h+1][j][a][b][k-1],dp[i][j][h][b][k-1] + dp[h+1][j][a][b][1]));
    }
    cout << dp[1][1][8][8][K];
}

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/110289873