搜索很重要,是很难学的算法,能看懂很简单,但是要想真正做出题来就比较困难了,那么,我们现在就水题开始研究搜索。
水题之:
1024: [SCOI2009]生日快乐
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 830 Solved: 572
[Submit][Status][Discuss]
Description
windy的生日到了,为了庆祝生日,他的朋友们帮他买了一个边长分别为 X 和
Y 的矩形蛋糕。 现在包括windy,一共有 N 个人来分这块大蛋糕,要求每个人必须获得相同面积的蛋糕。
windy主刀,每一切只能平行于一块蛋糕的一边(任意一边),并且必须把这块蛋糕切成两块。 这样,要切成 N 块蛋糕,windy必须切 N-1
次。 为了使得每块蛋糕看起来漂亮,我们要求 N 块蛋糕的长边与短边的比值的最大值最小。 你能帮助windy求出这个比值么?
Input
包含三个整数,X Y N。
Output
包含一个浮点数,保留6位小数。
Sample Input
5 5 5
Sample Output
1.800000
HINT
【数据规模和约定】
100%的数据,满足 1 <= X,Y <= 10000 ; 1 <= N <= 10
B站上不去的看这里~~~~TYVJ题目连接:http://www.tyvj.cn/Problem_Show.aspx?id=1771
没错这个真的是BZOJ的题,而且,不难做
我们乍一看好像没有什么思路,而实际上我们要求每一部分的面积相等就是要求:每次分割后得到的两个蛋糕的面积与分割后蛋糕应该再分的块数成正比。
好吧,好乱啊。实际上就是说:如果现在我们的这一块蛋糕分成面积比为2:3的两部分,那么我们下一步再分割蛋糕的时候两块蛋糕应该分割成的块数之比也应为2:3.
好了,这样我们就可以将某一状态确定下来了。
确定一个状态的因素:当前蛋糕的长x,蛋糕的宽y,这块蛋糕应该被分成的部分数k(正像某大神跟我说的:搜索的状态,题目给你什么你就往里面塞什么)
状态确定后,下一个问题就是如何实现状态的转移了。
不难发现,状态转移的过程就是切,分割当前的蛋糕。
然后我们仅需要枚举分割点就好了,这里比较难理解,结合代码说一下:
1 for(int i=1;i<k;i++) 2 { 3 ans=min(ans,max(dfs(x/k*i,y,i),dfs(x/k*(k-i),y,k-i))); 4 ans=min(ans,max(dfs(x,y/k*i,i),dfs(x,y/k*(k-i),k-i))); 5 }
就是这样了?没错,真的只有这么短。
好了,我们来看一下,我们可以理解为把我们的x*y的蛋糕分成单位为1*1的小块,然后枚举一下横着切分成i*y和(x-i)*y的两部分,然后递归求解这两块蛋糕的最优答案。对于y也是这样
边界:当只需要将当前的蛋糕分成1块的时候,返回长宽比就行了。
比较水,暴搜就能过
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int num; 5 double n,m; 6 double dfs(double x,double y,int k) 7 { 8 double ans=1e9; 9 if(x<y)swap(x,y); 10 if(k==1)return x/y; 11 else for(int i=1;i<k;i++) 12 { 13 ans=min(ans,max(dfs(x/k*i,y,i),dfs(x/k*(k-i),y,k-i))); 14 ans=min(ans,max(dfs(x,y/k*i,i),dfs(x,y/k*(k-i),k-i))); 15 } 16 return ans; 17 } 18 int main() 19 { 20 cin>>n>>m>>num; 21 double ans=dfs(n,m,num); 22 printf("%.6lf",ans); 23 return 0; 24 }