【训练题30:二维区间DP+二维差分】Cake slicing | UVA1629 | HDU 2513

Cake slicing | HDU 2513

难度

− 316 604 -\frac{316}{604} 604316
看网上的做法都是搜索,这里给出一份二维区间 D P DP DP 的吧。

题意

  • 给你一个 n × m n\times m n×m 的蛋糕。其中一些位置上有樱桃。
  • 你可以沿着网格切这个蛋糕,问你最少需要切多少单位,使得每个蛋糕上只有一个樱桃。
  • 每个蛋糕必须是矩形或方形。

数据范围

1 ≤ n , m ≤ 20 1\le n,m\le 20 1n,m20

思路

  • 看到 n , m n,m n,m 这么小,下意识就是典型的棋盘 D P DP DP 了。
  • d p [ i ] [ j ] [ i 2 ] [ j 2 ] dp[i][j][i2][j2] dp[i][j][i2][j2] 表示左上角坐标 ( i , j ) (i,j) (i,j) 到右下角坐标 ( i 2 , j 2 ) (i2,j2) (i2,j2) 的蛋糕切好最少花费。
  • 状态转移肯定是要枚举左上角顶点和右下角顶点的。然后再枚举切痕。
  • 但是这样,仔细思考的话会有问题。你切痕两边的矩形不一定事先算出来了。
  • 于是就是一个裸的二维区间 D P DP DP 的题目了。
    枚举横长,纵长,枚举左上角顶点,枚举切痕,时间复杂度 O ( n 5 ) = 3 e 6 O(n^5)=3e6 O(n5)=3e6 能接受。
  • 还有一点,边界条件是什么?那就是一个子蛋糕不用切了,即上面的樱桃数 ≤ 1 \le 1 1
  • 求子矩阵的樱桃数怎么快速算?诶对,就是二维差分
    具体操作可以看代码。

核心代码

时间复杂度 : O ( n 5 ) O(n^5) O(n5)
空间复杂度 : O ( n 4 ) O(n^4) O(n4)

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

int dp[MAX][MAX][MAX][MAX];
int de[MAX][MAX];
int aa[MAX][MAX];


void init(int a,int b){
    for(int i = 0;i <= a+1;++i)
    for(int j = 0;j <= b+1;++j){
        aa[i][j] = de[i][j] = 0;
        for(int i2 = 0;i2 <= a+1;++i2)
        for(int j2 = 0;j2 <= b+1;++j2)
            dp[i][j][i2][j2] = INF;
    }
}
void init2(int a,int b){			/// 差分数组初始化
    for(int i = 1;i <= a;++i)
    for(int j = 1;j <= b;++j){
        de[i][j] = aa[i][j] + de[i-1][j] + de[i][j-1] - de[i-1][j-1];
    }

}
int query(int x1,int y1,int x2,int y2){		/// 查询这个矩阵的包含樱桃数
    return de[x2][y2] - de[x2][y1-1] - de[x1-1][y2] + de[x1-1][y1-1];
}
int main()
{
    int ct = 0;
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k)){
        init(n,m);
        while(k--){
            int ta,tb;scanf("%d%d",&ta,&tb);
            aa[ta][tb] = 1;
        }
        init2(n,m);
        
        for(int L1 = 1;L1 <= n;++L1)
        for(int L2 = 1;L2 <= m;++L2)
        for(int i = 1;i+L1-1 <= n;++i)
        for(int j = 1;j+L2-1 <= m;++j){
            int i2 = i + L1 - 1;
            int j2 = j + L2 - 1;
            if(query(i,j,i2,j2) <= 1){		/// 边界
                dp[i][j][i2][j2] = 0;
                continue;
            }
            /// [i,j,i2,k]~[i,k+1,i2,j2]
            for(int k = j;k < j2;++k)
                dp[i][j][i2][j2] = min(dp[i][j][i2][j2],dp[i][j][i2][k] + dp[i][k+1][i2][j2] + i2-i+1);

            /// [i,j,k,j2]~[k+1,j,i2,j2]
            for(int k = i;k < i2;++k)
                dp[i][j][i2][j2] = min(dp[i][j][i2][j2],dp[i][j][k][j2] + dp[k+1][j][i2][j2] + j2-j+1);
        }

        printf("Case %d: %d\n",++ct,dp[1][1][n][m]);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值