习题选讲(分治法)
递归与分治 • Rate of Return • Binary Tree • sicily1935. 二叉树重建 • 电路稳定性 • Monthly Expense • Pie • sicily 1028. Hanoi Tower Sequence • 1211 商人的宣传 • 1071 Floors
1017 Rate of Return • 解题思路: • 当利润率在[0,1]之间,假设投资额和时间不变,则利润率越高,最后的本金和利润总和越大。 • 因此对利润率进行二分查找,检查在假设的利润率下,比较最后的本金和利润总和与实际值,调整二分上下界,直到达到某个精度。
1017 Rate of Return • double find(double left,double right,int m,double x) • { • double center,s=0; • center=(left+right)/2; • if (right-left<=0.000000005) • return center; • for (int i=1;i<=m;i++) • s+=a[i]*pow(1.0+center,m+1-i); • if (s<x) • return find(center,right,m,x); • else • return find(left,center,m,x); • }
Binary Tree Given a binary tree, every node of which contains one upper case character (‘A’ to ‘Z’); you just need to print all characters of this tree in pre-order. Input: 4 C 1 0表示标志符为4的点,点值为C,其左儿子为标识符为1的点,没有右孩子。3 4 C 1 3 1 A 0 0 3 B 0 0 Output: CAB
二叉树重建 • 题目大意: • 输入一棵二叉树的先序遍历序列和中序遍历序列,输出它的广度优先遍历序列。 • 例如,先序ABCDEF,中序CBAEDF
C D E F 二叉树重建 • 解题思路 • 前序遍历,根节点最先被输出,所以A是根结点。中序遍历,左子树的全部结点输出后,接着输出根结点,最后输出右子树的全部结点。 • 例如,先序ABCDEF,中序CBAEDF A B
B C D E F E C F E F B C D D 二叉树重建 • 左子树:前序BC,中序CB • 右子树:前序DEF,中序EDF A A A B
二叉树重建 void build(int n, intdep, char* s1, char* s2) { if (n <= 0) return; int p = strchr(s2, s1[0]) - s2; lv[dep][ln[dep]++] = s1[0]; build(p, dep+1, s1+1, s2); build(n-p-1, dep+1, s1+p+1, s2+p+1); } 调用:build(strlen(s1), 0, s1, s2);
电路稳定性 • 题目大意: • 给出一个电路,以及各元件断路的概率。求整个电路断路的概率。
电路稳定性 • 解题思路: • n个元件,若它们的断路概率为p1,p2,...,pn,则串联断路概率为1-(1-p1)(1-p2)...(1-pn),并联断路概率为p1p2...pn。 • 问题的关键点是把字符串表示的电路转化为实际电路。 • http://222.200.182.58/viewsource.php?sid=87438
Monthly Expense • 题目大意: • 农夫知道未来N天每天的花费,他现在需要把这N天分成连续的M段,使得花费最大的段花费最小。求这个花费。 • 1 <= N <= 100,000,1 <= M <= N
Monthly Expense • 解题思路: • 二分最高花费,再使用贪心的方法求出在这个花费下是否能把N天分成M段且每段都不超过这个花费,从而调整二分的上下界。
Monthly Expense • while(min<=max){ • mid=(min+max)/2; • int t=0;int num=0; • for(i=0;i<n;i++){ • if(num+N[i]<=mid)num+=N[i]; • else {t++; • if(t>m)break; • num=N[i];} • } • if(num)t++; • if(t<=m){ • ans=mid; • max=mid-1; • } • else min=mid+1; • }
Pie • 题目大意: • 一共有N个派和F+1个人,每个人分到一份派,要求每个人分到的派的大小相等,问最大的面积是多少。 • 1 ≤ N, F ≤ 10 000
Pie • 解题思路: • 二分派的大小,再使用贪心的方法求出在这个大小下,是否足够分给所有人,从而调整二分的上下界。
Pie • while(max-min>1e-5){ • mid=(min+max)/2.0; • int sum=0; • for(i=0;i<n;i++) • sum+=(int)(N[i]/mid); • if(sum<f) • max=mid; • else • min=mid; • }
1028 Hanoi Tower Sequence • 题目大意: • 定义汉诺塔,共有三个柱子和很多的大小两两不同的盘子放在一个柱子上,要求把它们移到另一个柱子上,每次只能移动一个放在柱子最顶端的盘子,并且每次移动后需保证较小的盘子在较大的盘子上面。给出步数p,求第p步移动的盘子的大小。 • p<=10^100
1028 Hanoi Tower Sequence • 解题思路: • n个盘子的汉诺塔问题递归求解:把前n-1个盘子移到第二根柱子上,把第n个盘子移到第三根柱子上,把前n-1个盘子移到第三根柱子上。 • 从一根柱子到另一根柱子,移动1个盘子需要f(1)=1步,移动k(k>1)个盘子需要f(k)=f(k-1)+1+f(k-1)步。 • 即得到f(k)=2^k-1。因此第2^k步移动的是k+1个盘子。在移动第k+1个盘子后,左右对移地移动k个盘子。
1028 Hanoi Tower Sequence • 把p化成二进制数,假设现在p里有超过1个位为1,设最高位为k,则在2^k步前和2^k步后对称,因此第p步移的盘子与第p-2^k步移的盘子一样。 • 直到p里只有1个位为1,设此时p=2^m,则此步移动的是第m+1个盘子。 • 因此题目转化成二进制数p,从低位起连续0的位数,加1,为答案。
1028 Hanoi Tower Sequence • int cal(int a[],int n) { • int cnt=1; • while (a[n-1]%2==0) { • cnt++; • for (int i=0,temp=0;i<n;i++) { • temp=temp*10+a[i]; • a[i]=temp/2; • temp%=2; • } • } • return cnt; • }
1211 商人的宣传 • 题目大意: • 有n个州,m条单向边,规定天数为L。 • 共有q个询问,每个询问为从州A到州B刚好为L步的方案数。
1211 商人的宣传 • 解题思路: • 第0天,每个州到自己的方案数为1。 • 第n+1天,每个州A到另一个州B的方案数为,对所有州C,第n天从A到C的方案数与一天内从C到B的方案数的积,再对所有州求和。(即第n天通过州C作中转的方案)
1211 商人的宣传 • int cal(int A,int B) { • int i,j,k; • memset(ans,0,sizeof(ans)); • ans[0][A][A]=1; • for (k=1;k<=L;k++) { • for (i=1;i<=n;i++) • for (j=1;j<=n;j++) • ans[k][A][i]+=ans[k-1][A][j]*edge[j][i]; • return ans[L][A][B]; • }
1071 Floors • 题目大意: • 一块长方形地板是由很多长方形瓷砖组成的,每次可以从中选一块,沿着瓷砖的边缘按直线切成两块,直到没有任何块可以继续此操作。求出此时最大的块的面积。
1071 Floors • 解题思路: • 对于每一块,尝试平行于长方形的两边分别切割。对于分出来的小块,递归进行重复操作。 • 判断是否可切割:按垂直于切割方向对瓷砖进行排序,切割线所切出来的块的面积与所在一侧的瓷砖总面积之和相等,则可切割。
1071 Floors • void cal(int x1,int y1,int x2,int y2,int l,int r) { • int s=(x2-x1)*(y2-y1); • if (s<=ans) • return; • if (cutx(x1,y1,x2,y2,l,r)) • return; • if (cuty(x1,y1,x2,y2,l,r)) • return; • if (s>ans) • ans=s; • }
1071 Floors • bool cmpx(const rect &a,const rect &b) { return a.xl<b.xl; } • bool cutx(int x1,int y1,int x2,int y2,int l,int r) { • sort(a+l,a+r+1,cmpx); • int x=x1,s=0; • for (i=l;i<r;i++) { • s+=a[i].s; • if (a[i].xh>x) x=a[i].xh; • if (s==(x-x1)*(y2-y1)) { • cal(x1,y1,x,y2,l,i); • cal(x,y1,x2,y2,i+1,r); • return true; • } • } • return false; • }
谢谢!