n*m的方格中有k个点,现在要把方格切开使每个点在一个部分。
对于一个长方形来说,有从它的长切开和从它的宽切开两种选择,每切一次花费边长的费用,求完成要求所花的最小费用
d p [ s x ] [ s y ] [ e x ] [ e y ] dp[sx][sy][ex][ey] dp[sx][sy][ex][ey]表示要把起点坐标(sx,sy),终点坐标(ex,ey)的矩形切开的最小花费
矩形内有0个点,则花费为INF
矩形内有1个点,花费为0
矩形内有>2个点
d p [ s x ] [ s y ] [ e x ] [ e y ] ( 按 长 切 开 ) = m i n ( d p [ s x ] [ s y ] [ i ] [ e y ] + d p [ i ] [ s y ] [ e x ] [ e y ] + e x − s x ) dp[sx][sy][ex][ey](按长切开)=min(dp[sx][sy][i][ey]+dp[i][sy][ex][ey]+ex-sx) dp[sx][sy][ex][ey](按长切开)=min(dp[sx][sy][i][ey]+dp[i][sy][ex][ey]+ex−sx)
d p [ s x ] [ s y ] [ e x ] [ e y ] ( 按 宽 切 开 ) = m i n ( d p [ s x ] [ s y ] [ e x ] [ j ] + d p [ s x ] [ j ] [ e x ] [ e y ] + e y − s y ) dp[sx][sy][ex][ey](按宽切开)=min(dp[sx][sy][ex][j]+dp[sx][j][ex][ey]+ey-sy) dp[sx][sy][ex][ey](按宽切开)=min(dp[sx][sy][ex][j]+dp[sx][j][ex][ey]+ey−sy)
∀ i ∈ ( s x , e x ) , ∀ j ∈ ( s y , e y ) \forall i \in(sx,ex),\forall j \in(sy,ey) ∀i∈(sx,ex),∀j∈(sy,ey)
#include <cstdio>
#include <cstring>
#include <iostream>
#define ll long long
using namespace std;
const int maxm=20+10;
const int INF=1e7;
int n,m,k;
int dp[maxm][maxm][maxm][maxm];
int map[maxm][maxm];
int judge(int sx,int sy,int ex,int ey){
int res=0;
for(int i=sx+1;i<=ex;i++){
for(int j=sy+1;j<=ey;j++){
if(map[i][j]) res++;
if(res==2) return 2;
}
}
return res;
}
int DP(int sx,int sy,int ex,int ey){
if(dp[sx][sy][ex][ey]!=-1) return dp[sx][sy][ex][ey];
if(judge(sx,sy,ex,ey)==0) return dp[sx][sy][ex][ey]=INF;
if(judge(sx,sy,ex,ey)==1) return dp[sx][sy][ex][ey]=0;
int &tmp=dp[sx][sy][ex][ey];
tmp=INF;
for(int i=sx+1;i<ex;i++){
tmp=min(tmp,DP(sx,sy,i,ey)+DP(i,sy,ex,ey)+ey-sy);
}
for(int i=sy+1;i<ey;i++){
tmp=min(tmp,DP(sx,sy,ex,i)+DP(sx,i,ex,ey)+ex-sx);
}
return tmp;
}
int main(){
int kase=0;
while(~scanf("%d%d%d",&n,&m,&k)){
memset(map,0,sizeof(map));
memset(dp,-1,sizeof(dp));
for(int i=0;i<k;i++){
int x,y;
scanf("%d%d",&x,&y);
map[x][y]=1;
}
printf("Case %d: %d\n",++kase,DP(0,0,n,m));
}
}
Ⅰ:
记忆化(int DP()中的前三行)
Ⅱ:
矩形分割处理