一道动态规划题,没什么可说的。
定义DP[i][j][k][l[]表示以i,j为顶点,长度为k宽度为l的蛋糕最少需要的代价。
对于一个dp[i][j][k][l]来说,你既可以横着切,也可以竖着切。状态转移方程有两个。
对于一个顶点在(row,col),长度为len,宽度为wid的矩形来说,
竖:dp[row][col][len][wid]=min(dp[row][col][len][wid],wid+dp[row][col][i][wid]+dp[row][col+i][len-i][wid]);
横:dp[row][col][len][wid]=min(dp[row][col][len][wid],len+dp[row][col][len][i]+dp[row+i][col][len][wid-i]);
然后走记忆化搜索就可以了。
不过在实际上写的时候还需要注意一个问题就是,判断切了之后的每个小矩形里面还有多少个樱桃,我这里在切之前会先判断一下,看看切了之后的两个小矩心是不是都有樱桃,用了一个二维的树状数组,但是实际上我觉得用二维前缀和更好,O(1)的时间嘛。我这里用二维树状数组主要是想练一练手。。
Tips:我这里的_insert()和__query()是用于树状数组的,getCheey()拿到一个矩形里面有多少个Cheey,_search用于记忆化搜索。
PS:樱桃的单词本来是Cherry,但是我记成了Cheey。。。。。好吧,大家将就着看一看吧。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef pair<int,int> pa;
int n,m,k,ca=1,arr[30][30][30][30],cheey[30][30],_search(int row,int col,int len,int wid);
pa te;
void _insert();
int _query(int row,int col),getCheey(int row,int col,int len,int wid);
int main(){
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
memset(arr,0x3f,sizeof arr);
for(int i=0;i<k;++i)
scanf("%d%d",&te.first,&te.second),_insert();
printf("Case %d: %d\n",ca++,_search(1,1,m,n));
memset(cheey,0,sizeof cheey);
}
return 0;
}
int _search(int row,int col,int len,int wid){
int &te=arr[row][col][len][wid];
if(te!=0x3f3f3f3f)
return te;
if(getCheey(row,col,len,wid)==1)
return te=0;
for(int i=1;i<len;++i)
if(getCheey(row,col,i,wid)&&getCheey(row,col+i,len-i,wid))
te=min(te,wid+_search(row,col,i,wid)+_search(row,col+i,len-i,wid));
for(int i=1;i<wid;++i)
if(getCheey(row,col,len,i)&&getCheey(row+i,col,len,wid-i))
te=min(te,len+_search(row,col,len,i)+_search(row+i,col,len,wid-i));
return te;
}
void _insert(){
for(int i=te.first;i<=n;i+=(i&-i))
for(int j=te.second;j<=m;j+=(j&-j))
++cheey[i][j];
}
int _query(int row,int col){
int ans=0;
for(int i=row;i;i-=(i&-i))
for(int j=col;j;j-=(j&-j))
ans+=cheey[i][j];
return ans;
}
int getCheey(int row,int col,int len,int wid){
return _query(row+wid-1,col+len-1)+_query(row-1,col-1)-_query(row-1,col+len-1)-_query(row+wid-1,col-1);
}