|
试题描述
|
|
如图所示有一个矩形蛋糕,上面划分成了n行m列的网格,一些网格内放着樱桃。现在要根据如下规则切蛋糕:
1.切开的每一块必须是矩形(包括正方形) 2.切蛋糕时必须沿着网格线,不能拐弯 3.切开的每一块蛋糕上有且仅有一个樱桃 下图是一种切割方法:
这种方法需要切割的边数为2+4=6 以下是另一种切割方法:
这种方法需要切割的边数为3+2=5 现在给定蛋糕的形状和上面樱桃的分布,要求求出切割边数最少的方案。 |
|
输入
|
|
第一行包含三个正整数n,m和k(1<=n,m<=20),k表示樱桃数量
以下k行每行包含两个正整数,表示每个樱桃所在的行和列 |
|
输出
|
|
输出最优方案的切割边数
|
|
输入示例
|
|
3 4 3
1 2 2 3 3 2 |
|
输出示例
|
|
5
|
思路:明显一个递归,相信各位大佬忧虑的是如何在dfs中储存这个区域,我推荐一个方法,可以int dfs(int x,int y,int w,int h)其中x,y表示左上的点的坐标,而w,h表示这个矩形的 长和宽,再加个记忆化,dp[30][30][30][30];就可以再优化。
详解见代码注释:
#include<bits/stdc++.h>//万能头文件
using namespace std;
int a[30][30];
int dp[30][30][30][30];//记忆化搜索的dp
int n,m;
int k;
int dfs(int x,int y,int w,int h)//dfs函数
{
int cnt=0;//cnt表示该蛋糕上的樱桃数
for(int i=x+1;i<=x+w;i++)
{
for(int j=y+1;j<=y+h;j++)
{
if(a[i][j]) cnt++;//统计樱桃数
}
}
if(cnt==0) return 999999999;//如果蛋糕上的樱桃数为0,999999999代表inf
if(cnt==1) return 0;//如果蛋糕上的樱桃数为1,直接return 0
if(dp[x][y][w][h]!=-1) return dp[x][y][w][h];//如果之前搜过,就不用搜了,直接return掉
int ans=2147483647;//ans初值为最大值
for(int i=1;i<w;i++)//搜横行
{
ans=min(ans,dfs(x,y,i,h)+dfs(x+i,y,w-i,h)+h);//min一下
}
for(int i=1;i<h;i++)//同上
{
ans=min(ans,dfs(x,y,w,i)+dfs(x,y+i,w,h-i)+w);
}
dp[x][y][w][h]=ans;//赋值
return ans;
}
int main()
{
memset(dp,-1,sizeof(dp));//给dp赋初值
scanf("%d%d%d",&n,&m,&k);
int p,q;
for(int i=1;i<=k;i++)
{
scanf("%d%d",&p,&q);
a[p][q]=1;
}
cout<<dfs(0,0,n,m);//dfs一下
return 0;
}


