切蛋糕 Cake slicing

UVA1629
定义
U p p e r L e f t UpperLeft 为矩形左上角, L o w e r R i g h t LowerRight 为右下角。
d p [ U p p e r L e f t . x ] [ U p p e r L e f t . y ] [ L o w e r R i g h t . x ] [ L o w e r R i g h t . y ] dp[UpperLeft.x][UpperLeft.y][LowerRight.x][LowerRight.y] 为由两组坐标确定的矩形区域最少要切割多少长度才能满足要求。
P r e f i x S u m [ i ] [ j ] PrefixSum[i][j] 为从(0,0)到(i,j)的矩形区域一共有多少樱桃。(用于O(1)计算出给定矩形区域内樱桃数量)。
初始化
d p = i n f dp=inf
转移方程
d p [ U p p e r L e f t . x ] [ U p p e r L e f t . y ] [ L o w e r R i g h t . x ] [ L o w e r R i g h t . y ] = m i n ( d p [ U p p e r L e f t . x ] [ U p p e r L e f t . y ] [ i ] [ L o w e r R i g h t . y ] + d p [ i + 1 ] [ U p p e r L e f t . y ] [ L o w e r R i g h t . x ] [ L o w e r R i g h t . y ] + L o w e r R i g h t . y U p p e r L e f t . y + 1 ( U p p e r L e f t . x i < L o w e r R i g h t . x , ) , d p [ U p p e r L e f t . x ] [ U p p e r L e f t . y ] [ L o w e r R i g h t . x ] [ i ] + d p [ U p p e r L e f t . x ] [ i + 1 ] [ L o w e r R i g h t . x ] [ L o w e r R i g h t . y ] + L o w e r R i g h t . x U p p e r L e f t . x + 1 ( U p p e r L e f t . y i < L o w e r R i g h t . y , ) dp[UpperLeft.x][UpperLeft.y][LowerRight.x][LowerRight.y]=min(\\ dp[UpperLeft.x][UpperLeft.y][i][LowerRight.y]\\+dp[i+1][UpperLeft.y][LowerRight.x][LowerRight.y]\\+LowerRight.y-UpperLeft.y+1(UpperLeft.x\leq i<LowerRight.x,横着切),\\ dp[UpperLeft.x][UpperLeft.y][LowerRight.x][i]\\+dp[UpperLeft.x][i+1][LowerRight.x][LowerRight.y]\\+LowerRight.x-UpperLeft.x+1(UpperLeft.y\leq i<LowerRight.y,竖着切)
AC代码

#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
using namespace std;
int n, m, k;
bool HasCherry[21][21];
int PrefixSum[22][22];
int dp[22][22][22][22];
constexpr static int inf = 0x3f3f3f3f;
struct Point {
	int x, y;
};
bool Input() {
	if (scanf("%d%d%d", &n, &m, &k) == EOF) {
		return false;
	}
	memset(HasCherry, false, sizeof(HasCherry));
	while (k--) {
		int x, y;
		scanf("%d%d", &x, &y);
		HasCherry[x][y] = true;
	}
	return true;
}
void InitPrefixSum() {
	memset(PrefixSum, 0x0, sizeof(PrefixSum));
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			PrefixSum[i][j] = PrefixSum[i - 1][j] + PrefixSum[i][j - 1] - PrefixSum[i - 1][j - 1] + HasCherry[i][j];
		}
	}
}
const int getCherryNumber(const Point& UpperLeft,const Point& LowerRight) {
	return
		PrefixSum[LowerRight.x][LowerRight.y]
		- PrefixSum[LowerRight.x][UpperLeft.y - 1]
		- PrefixSum[UpperLeft.x - 1][LowerRight.y]
		+ PrefixSum[UpperLeft.x - 1][UpperLeft.y - 1];
}
int DP(Point UpperLeft, Point LowerRight) {
	int& Ans = dp[UpperLeft.x][UpperLeft.y][LowerRight.x][LowerRight.y];
	const int&& CherryNumber = getCherryNumber(UpperLeft, LowerRight);
	//樱桃数量为0,不符合题意,返回inf
	if (!CherryNumber) {
		return Ans = inf;
	}
	//不需要切了,返回0
	else if (CherryNumber == 1) {
		return Ans = 0;
	}
	if (Ans != inf) {
		return Ans;
	}
	//横着切,枚举每一种切割方案
	for (int i = UpperLeft.x; i < LowerRight.x; ++i) {
		Ans = min(
			Ans, 
			DP(UpperLeft, { i,LowerRight.y }) 
			+ DP({ i + 1,UpperLeft.y }, LowerRight) 
			+ LowerRight.y - UpperLeft.y + 1
		);
	}
	//竖着切
	for (int i = UpperLeft.y; i < LowerRight.y; ++i) {
		Ans = min(
			Ans,
			DP(UpperLeft, { LowerRight.x,i })
			+ DP({ UpperLeft.x,i + 1 }, LowerRight)
			+ LowerRight.x - UpperLeft.x + 1
		);
	}
	return Ans;
}
int main() {
	int&& Case = 0;
	while (Input()) {
		InitPrefixSum();
		memset(dp, 0x3f, sizeof(dp));
		printf("Case %d: %d\n", ++Case, DP({ 1,1 }, { n, m }));
	}
	return 0;
}
发布了30 篇原创文章 · 获赞 33 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42971794/article/details/104053338