uva - 1629 Cake slicing(dp)

蛋糕切割问题DP算法
本文介绍了一种使用动态规划解决蛋糕切割问题的方法,目标是最小化切割长度使得每块蛋糕上恰好有一颗樱桃。通过逆向思考将问题转化为网格蛋糕的拼接过程,并给出具体的实现代码。

题意:n行m列的网格蛋糕上有一些樱桃,每次可以用刀沿着网格线切成两块,问要想使最终每块蛋糕上恰好有一颗樱桃,切得最小长度。

直接正向去想怎样切感觉不好想,但是可以反过来,像区间拼接一样考虑把当个网格的蛋糕拼接起来,拼接的长度就是要切的长度。

dp[a][b][c][d]表示从(a, b)点到(c, d)的矩形范围内拼接的最短长度,num[a][b][c][d]用来统计这个矩形区域内的樱桃数量。如果这一块区域内只有一块(或者没有)樱桃那么拼接长度一定是0。对于一块区域可以是上下两个矩形也可以是左右两个矩形拼接起来的,那么枚举拼接的位置就可以了,注意如果拼接的两个矩形有一个中没有樱桃是不符合条件的,要跳过。

整个思想和区间dp一致,只是区间dp是一维的,而这个是二维的。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int dp[21][21][21][21];
int num[21][21][21][21];
int main() {
    int ks = 1, n, m, q, r, c;
    while(~scanf("%d%d%d", &n, &m, &q)) {
        memset(num, 0, sizeof(num));
        memset(dp, 0x3f, sizeof(dp));
        while(q--) {
            scanf("%d%d", &r, &c);
            num[r - 1][c - 1][r][c] = 1;
        }
        for(int rl = 1; rl <= n; rl++) {
            for(int cl = 1; cl <= m; cl++) {
                for(int br = 0; br + rl <= n; br++) {
                    for(int bc = 0; bc + cl <= m; bc++) {
                        int er = br + rl, ec = bc + cl;
                        num[br][bc][er][ec] = max(num[br][bc][br + 1][ec] + num[br + 1][bc][er][ec], num[br][bc][er][bc + 1] + num[br][bc + 1][er][ec]);
//                        printf("%d %d %d %d %d\n", br, bc, er, ec, num[br][bc][er][ec]);
                        if(num[br][bc][er][ec] <= 1) {
                            dp[br][bc][er][ec] = 0;
                            continue;
                        }
                        for(int j = br + 1; j < er; j++) {
                            if(!num[br][bc][j][ec] || !num[j][bc][er][ec]) continue;
                            if(dp[br][bc][j][ec] + dp[j][bc][er][ec] + cl < dp[br][bc][er][ec]) {
                                dp[br][bc][er][ec] = dp[br][bc][j][ec] + dp[j][bc][er][ec] + cl;
                            }
                        }
                        for(int j = bc + 1; j < ec; j++) {
                            if(!num[br][bc][er][j] || !num[br][j][er][ec]) continue;
                            if(dp[br][bc][er][ec] > dp[br][bc][er][j] + dp[br][j][er][ec] + rl)
                                dp[br][bc][er][ec] = dp[br][bc][er][j] + dp[br][j][er][ec] + rl;
                        }
                    }
                }
            }
        }
        printf("Case %d: %d\n", ks++, dp[0][0][n][m]);
    }
    return 0;
}
/**
3 4 3
1 2
2 3
3 2
**/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值