难度
− 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 1≤n,m≤20
思路
- 看到 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;
}