UVa 1629 Cake Slicing DP

这题空了好久,虽然现在想出来了很激动,但是花的时间略长呀。。。
每切一次蛋糕就相当于一次状态转移。设某时刻蛋糕的坐上顶点为(a, b),右下顶点为(c, d),将这块蛋糕切成只剩一个樱桃产生的切割线长度最短为d ( a, b, c, d)。
原来是想根据樱桃的位置来切蛋糕,但是很难实现。考虑到横竖共m + n 种切法,可以枚举状态。
纵向:min { d ( a, b, c, j ) + d ( a, j + 1, c, d ) + d - b + 1 } ( b <= j < d )
横向:min { d ( a, b, i, d ) + d ( i + 1, b, c, d ) + c - a + 1 } ( a <= i < c )
枚举时需要判断:当矩形内只有一个点时,d() 值为0;当切割出的矩形不含樱桃时,舍弃此种情况。d[][][][]初始化为-1。

代码:
#include <cstdio>
#include <cstring>

const int maxn = 25;

int n, m, k;
int dp[maxn][maxn][maxn][maxn], rec[maxn][maxn];

int dpl(int a, int b, int c, int d) {
        if (dp[a][b][c][d] > -1) return dp[a][b][c][d];
        int &res = dp[a][b][c][d];
        int dot = rec[c][d] + rec[a - 1][b - 1] - rec[a - 1][d] - rec[c][b - 1];
        //printf("1:%d,%d    %d,%d     value:%d\n", a, b, c, d, res);
        if (dot == 1) return res = 0;
        int min = 1000000;
        for (int j = b; j < d; j++) {
                // divide into d(a, b, c, j) and d(a, j + 1, c, d)
                dot = rec[c][j] + rec[a - 1][b - 1] - rec[a - 1][j] - rec[c][b - 1];
                if (dot == 0) continue;
                dot = rec[c][d] + rec[a - 1][j] - rec[a - 1][d] - rec[c][j];
                if (dot == 0) continue;
                int t = dpl(a, b, c, j) + dpl(a, j + 1, c, d) + c - a + 1;
                if (t < min) min = t;
        }
        for (int i = a; i < c; i++) {
                // divide into d(a, b, i, d) and d(i + 1, b, c, d)
                dot = rec[i][d] + rec[a - 1][b - 1] - rec[a - 1][d] - rec[i][b - 1];
                if (dot == 0) continue;
                dot = rec[c][d] + rec[i][b - 1] - rec[i][d] - rec[c][b - 1];
                if (dot == 0) continue;
                int t = dpl(a, b, i, d) + dpl(i + 1, b, c, d) + d - b + 1;
                if (t < min) min = t;
        }
        res = min;
        //printf("2:%d,%d    %d,%d     value:%d\n", a, b, c, d, res);
        return res;
}

int main() {
        #ifndef LOCAL
                freopen("in.txt", "r", stdin);
        #endif // LOCAL
        int kase = 1;
        while (~scanf("%d%d%d", &n, &m, &k)) {
                memset(rec, 0, sizeof(rec));
                memset(dp, -1, sizeof(dp));
                int x, y;
                for (int i = 0; i < k; i++) {
                        scanf("%d%d", &x, &y);
                        rec[x][y] = 1;
                }
                for (int i = 1; i <= n; i++)
                        for (int j = 1; j <= m; j++) {
                                rec[i][j] += rec[i - 1][j];
                                //printf("%d%c", rec[i][j], " \n"[j == m]);
                        }
                for (int i = 1; i <= n; i++)
                        for (int j = 1; j <= m; j++) {
                                rec[i][j] += rec[i][j - 1];
                                //printf("%d%c", rec[i][j], " \n"[j == m]);
                        }

                printf("Case %d: %d\n", kase++, dpl(1, 1, n, m));
        }
        return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值