UVA1629
定义:
U
p
p
e
r
L
e
f
t
UpperLeft
UpperLeft为矩形左上角,
L
o
w
e
r
R
i
g
h
t
LowerRight
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]
dp[UpperLeft.x][UpperLeft.y][LowerRight.x][LowerRight.y]为由两组坐标确定的矩形区域最少要切割多少长度才能满足要求。
P
r
e
f
i
x
S
u
m
[
i
]
[
j
]
PrefixSum[i][j]
PrefixSum[i][j]为从(0,0)到(i,j)的矩形区域一共有多少樱桃。(用于O(1)计算出给定矩形区域内樱桃数量)。
初始化:
d
p
=
i
n
f
dp=inf
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,竖着切)
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≤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≤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;
}