题意:n*m大小的蛋糕,被分成n*m个区域,其中k个区域上面有樱桃。你需要把蛋糕切成k份,每份都有樱桃,切的时候只能水平或竖直沿着网格线切,切到底。问最短切的长度是多少。
思路:DP。DP[u][d][l][r]。u,d,l,r表示整块蛋糕的上下左右。一块大蛋糕的最优切法一定也是它分成两块以后的最优切法,枚举每种切法记忆化搜索可解。状态转移方程见代码。
#include <iostream> #include <stdio.h> #include <cmath> #include <algorithm> #include <iomanip> #include <cstdlib> #include <string> #include <memory.h> #include <vector> #include <queue> #include <stack> #include <ctype.h> #define INF 1000000 using namespace std; bool map[22][22]; int DP[22][22][22][22]; int n,m,k; int judge(int u,int d,int l,int r){//判断区域内是否有樱桃,2表示有多个 int re=0; for(int i=u+1;i<=d;i++){ for(int j=l+1;j<=r;j++){ if(map[i][j]){ re++; if(re==2)return 2; } } } return re; } int fun(int u,int d,int l,int r){ if(DP[u][d][l][r]!=-1)return DP[u][d][l][r]; if(judge(u,d,l,r)==1){ DP[u][d][l][r]=0; return 0; } if(!judge(u,d,l,r)){ DP[u][d][l][r]=INF; return INF; } int re=INF; for(int i=u+1;i<d;i++){//横切 re=min(re,fun(u,i,l,r)+fun(i,d,l,r)+r-l); } for(int i=l+1;i<r;i++){//竖切 re=min(re,fun(u,d,l,i)+fun(u,d,i,r)+d-u); } DP[u][d][l][r]=re; return re; } int main(){ int c=1; while(cin>>n>>m>>k){ memset(DP,-1,sizeof(DP)); memset(map,0,sizeof(map)); int nn,mm; for(int i=1;i<=k;i++){ cin>>nn>>mm; map[nn][mm]=1; } cout<<"Case "<<c<<": "<<fun(0,n,0,m)<<endl; c++; } return 0; }