幻曦紫嶙 幻曦紫嶙
关注数: 36 粉丝数: 160 发帖数: 7,158 关注贴吧数: 187
【1021】【buy low,buy lower】解题报告 program buylow; var n:longint; stock:array[1..5000]of longint; dp:array[1..5000]of longint; sum:array[1..5000]of longint;{存放方案数} flag:array[1..5000] of boolean;{标记重复方案} maxnum,maxplan:longint; {最大购买次数、最大购买方案数} procedure fstart; {输入输出准备} begin assign(input,'d:\test\data1.in'); reset(input); assign(output,'d:\test\data1.out'); rewrite(output); end; procedure fend; {结束工作} begin close(input); close(output); end; procedure input; var i:longint; begin readln(n); for i:=1 to n do read(stock[i]); fillchar(flag,sizeof(flag),true); end; procedure MaxBuyNum; var i,j:longint; begin maxnum:=0; maxplan:=0;{} {求最长下降序列} for i:=1 to n do begin dp[i]:=1; sum[i]:=1; for j:=1 to i-1 do begin if stock[i]=stock[j] then flag[j]:=false; {如果出现相同的数,则选择后面的,前面的数标记不能用} if (stock[i]<stock[j])and(flag[j]) then if (dp[i]<dp[j]+1) then begin dp[i]:=dp[j]+1 ; sum[i]:=sum[j];{不是同一次可以购买的} end else if(dp[i]=dp[j]+1)then inc(sum[i],sum[j]); {同一次可以购买的则方案数累加} end; if dp[i]>maxnum then maxnum:=dp[i]; {找最大购买次数} end; {求最大购买方案数} for i:=1 to n do if(maxnum=dp[i])and(flag[i])then inc(maxplan,sum[i]);{最后一次购买的方案累加} end; procedure output; var i:longint; begin writeln(maxnum,' ',maxplan); for i:=1 to n do write(sum[i]:3); writeln; readln;readln; end; begin fstart; input; MaxBuyNum; output; fend; end.
【1064】 【分治-最近点对距离问题】 |【分治思路】 1. 简述 给定平面上N个点的坐标,找出距离最近的两个点。 2. 思路 暴力的方法是C(N,2),N^2的复杂度。 二分的方法,比如首先将所有的点根据横坐标排序,递归二分。终止条件:直到只有一个点或者两个点的时候返回,一个点时返回空,两个点时返回这两个点构组对。递归过程:先计算左边N/2个点之间的最近点对,计算右边N/2个点之间的最近点对,然后计算左边点与右边点构成的最近点对。这样复杂度公式:T(n) = 2T(n/2) + n^2/4,根据主定理,得到T(n)=n^2/4,还是N^2级别的。 二分的方法进一步化简,复杂度都浪费在(左边的某个点,右边的某个点)这的组合上了。假设MinDistLeft=左边最近点对的距离,MinDistRight=右边最近点对的距离,这样已知的最近点对距离可以表示为C=Min{MinDistLeft, MinDistRight},左边的最后一个点坐标为(x1, y1),右边第一个点的坐标为(x2, y2),那么左边合法的点的横坐标必须在(x2-C, x2]的范围内,右边合法的点的横坐标必须是在[x1, x1+C)的范围内,因为如果来个点横坐标就相差了C以上的话,那么距离比如大于C,因此这样做完之后,需要判断的点对的数量可能就会减少不少。 二分的方法再进一步化简,前一步化简化将左右两边的点控制在分别控制在(x2-C, x2)和[x1,x1+C)的范围内,实际上x1<=x2,因此就相当于一个2*C的两条竖线之间的点。前一步是考虑横坐标,这一步该考虑纵坐标了,对于左边和右边的点合在一个列表并按照纵坐标排序,并且标记每个点是左边的还是右边的。然后遍历这个列表,对于当前点只计算这个点后面的不同来自不同方向且纵坐标差小于C的点,如果距离超过了C,那么当前点计算停止。即每个点考虑的是其对角方向的一个C*C方框内的点,根据编程之美上的说法,这样的点只有四个,因为如果超过4个的话(此时应该是方框的4个脚),那么在前面递归分别左右两边的时候,就能得到比C更小的值了,因此遍历列表的计算次数应该是每个最多计算4次,当然算上自己这边的方框的话,那么最多比较7次(那3次就是自己这边的点)。 好了算算复杂度,这里暂时假设范围控制后,点的个数没减少,那么排序就是n*logn,遍历列表就是n*7。T(n) = 2T(n/2) + n*logn + 7*n。根据主定理,复杂度为n*logn+7*n,是n*logn级别的。 3. 代码 不写了,最近时间太紧了,后面会尽量写一点好写的,这个主要是测试数据不太好搞。 4. 扩展问题 第一个是给一个数组,求相邻数值之间的差值的最大值。所谓相邻数值,就是对于X,Y两个数值,不存在数组中其他的数在[X,Y]范围内。一般就是排序,然后前后两个数值的差来更新最大的差值。书上用了抽屉原理+桶子法,先计算出数组中最大值和最小值,可知最大差值比然大于等于delta=(Max-Min)/(N-1),这个很好证明,反证法,如果最大差值都小于delta,那么N个数值,中间N-1个差值,最小值和最大值的差就小于(delta*(N-1)),即最小值和最大值的差小于(Max-Min),矛盾了。然后把[Min,Max]分为N份,然后把不同的数值放到对应桶子里,然后只考虑前后非空的两个桶子的前一个桶子的最后一个数值与后一个桶子的第一个数值的差值即可。 对于一个数值,可以把x映射到(x-Min)/delta号桶子中。由于可能有多个数值在一个桶子中,因此用一个邻接链表可能更好,每个链表内部记录最小值和最大值。其实当最大值和最小值差值较小的时候,用计数排序的方法也可以的,就是开Max-Min+1长度的空间,每个数值可以直接放进去,计算不为空的两个数之间的差。不过计数排序的性能依赖于最大值与最小值的差的大小,以及数值要整数。 第二个题目是给定平面上的N个点,求距离最远的两个点。这个题目没多想,感觉用二分也是可以的,细想一下两次化简都不行,因为即使两个点横坐标或者纵坐标差小于C,两个点的距离仍旧肯能超过C。寻找最远点对(一)这篇博文上面的方法好像是先按照极角排序,然后求凸包,紧着用卡壳旋转方法,复杂度能到n*logn,卡壳旋转好像不是一下能看懂的,貌似是计算几何方面的,暂时这样了,以后研究吧。 5. 参考 编程之美,2.11节,寻找最近点对
【1379】 【分治-消除隐藏线】 |【分治思路】 【题目分析】 本题其实是矩形覆盖问题的特殊情形——固定了矩形的下边界。本题可以使用矩形切割或者离散化加上线段树解决,但是前者的时间复杂度在最坏情况下可能达到O(n3)[1],而后者的编程实现比较复杂。 本文将介绍一种时间复杂度稳定在O(nlogn),且编程比较简单的分治算法。 这种算法的思路是:要求n个矩形的轮廓,先将这n个矩形分成两个大小相等的部分,分别求其轮廓,然后再将这两个轮廓合并。 规模为1的问题可以直接解决。具体来说,如果这个矩形的三元组表示为(L,H,R),那么其轮廓为(L,H,R,0)。 对于规模为k的问题,假设得到了两个规模为k/2的轮廓,分别为A和B,我们如何得到合并后的轮廓C?首先,容易证明轮廓C的每一个横坐标,都来源于轮廓A和B的横坐标,而不会产生新的坐标值。因此,我们只需计算A和B中所有涉及到的横坐标在C中的高度。由于轮廓C中的横坐标值要求有序,我们可以仿照归并排序的方法,用两个指针扫描轮廓A和B。具体的方法是,设指针i指向轮廓A的当前横坐标,指针j指向轮廓B的当前横坐标。 如果指针i指向的横坐标较小,那么将这一横坐标加入到C中,且在C中的高度为A中第i个横坐标对应的高度与B中第j-1个横坐标对应的高度的最大值,然后将指针i向后移一位;指针j指向的横坐标较小的情况则类似处理。 如果两个指针指向的横坐标相同,此时只需将这一横坐标加入到C中一次,且高度为两指针指向高度的最大值,然后将两指针同时向后移一位。 最后,需要扫描一遍轮廓C,将相邻的高度相同的横坐标合并。 分析时间复杂度,设T(n)表示解决规模为n的问题需要的时间,那么有。解此递归方程,得到T(n)=O(nlogn)。空间方面,由于递归的层数为O(logn),每一层需要保存O(n)的空间,所以总的空间复杂度为O(nlogn)。 【性能分析】 时间复杂度:O(nlogn) 空间复杂度:O(nlogn) 编程复杂度:低 【总结】 本题可以采用多种方法解决,本文介绍的分治方法的优势在于:编程简单、时间复杂度低。 [1] 具体分析参见2004年集训队论文:薛矛《解决动态统计问题的两把利刃——剖析线段树与矩形切割》
【1064】 【分治-最近点对距离问题】 |【分治思路】 问题描述: 给定平面上n个点,找出距离最近的两个点。 思考过程: 1)对于这种问题,我们首先想到的求解方法就是求出所有点对的距离,并找出最近的那个,当然这个是个显而易见的方法,具体过程大体可以警醒如下描述。 ① 定义变量 N (点的数目),X[N] (点的x坐标值),Y[N](点的y坐标值),closest(最近的值),P(最近点对的一个点),Q(最近点对的另一个点)。 ② 依次扫描并比较出最近点对 初始化closest=get_len(X[1],Y[1],X[2],Y[2]); P=(X[1],Y[1]);Q=(X[2],Y[2]); For i=1:N For j=i+1:N Tmp=get_len(X,Y,X[j],Y[j]); If Tmp < closest Closest=Tmp; P=(X,Y); Q= (X[j],Y[j]); End If End For End For 这样运行后就可以求出所有的点中的最近的点对值,然而这是一种Brute-force(蛮力法),当然时间复杂度也是非常不能令人满意的Q(n2)。 2)对于1)中的算法基本上很简单而且很容易想到,的确,对于一些问题,人们往往想的简单做的难,想的难就做的简单啦(呵呵)。在此,可以利用分治法来对问题求解。具体算法步骤如下: ① 将所有的点按X坐标值排序,然后平均等分成两个部分PL与PR。在左右分开处画个分割线part_line。(如下图)② 找出PL中的最近值与PR中的最近值分别为dL,dR。 ③ 令d=min(dL,dR);现在,问题来了,d一定最小么? 回答是相当确定的,就是肯定不是最小的。原因也显而易见,就是最近点对可能出现在PL与PR的分割处。 ④ 根据③中的问题,可以以part_line为中心左右|d|的距离里的点考虑(为什么?超过这个距离显然不需要 考虑啦,因为距离一定会大于d啦~~~)。现在,问题又来了,如何取得这个范围内的最近点呢?蛮力?全搜索计算?对!不行的这是,可能所有的点都落在这个范围里,就此一项我的时间复杂度就又会回到Q(n2)。怎么办? ⑤ 对于④中的问题,解决方法是这样的,将左右|d|范围里的点按Y值排序,然后依次从每个点出发做水平线,并在其之上d的距离做水平线,这样得到了一个2d*d的矩形,显然,在此矩形之外的点无需开率啦,可以证明,矩形内最多容下8个点(包括自己本身)。所以意味着在排好序的Y坐标点中,只需要考虑其后的7个点就行了,此外已有人证明了事实上只需要考虑其后的4个点就行啦。可见,这些的时间复杂度最多Q(N)。 ⑥ 纵观整个过程,首先要对X坐标排序时间为O(NlogN),这是分治之前的预处理。然后问题分成了N/2规模,然后用Q(N)的复杂度得到中间的最近点对,然后可以在常数时间内得到最终的点对与最近值。So。。。T(N)=2T(N/2)+O(N),可以得到该算法的时间为O(NlogN)(即使加上一开始的排序预处理的时间复杂度)。
首页 1 2 3 下一页