不是情况洗钱分为哪两种方式两种吗?一个是n一个是n-1这里怎么判断出来它是n-1的?

招聘公众号
您所在的位置:>> 新闻详情
N、N+1、2N到底是什么?一次讲明白经济补偿金!
文章来源:劳动报
网友经常在后台问我们什么是经济补偿金中的N、N+1、2N?它们适用于哪些情况?解除劳动合同时&经济补偿该怎么算?经济补偿金主要由两个因素决定:&员工工资和工作年限。《劳动合同法》从日开始实施,根据法不溯及既往的原则,对于跨越2008年的经济补偿金就存在分段计算的问题。2008年后,经济补偿按劳动者在本单位工作的年限——每满一年支付一个月工资的标准向劳动者支付。六个月以上不满一年的,按一年计算;不满六个月的,向劳动者支付半个月工资的经济补偿。劳动者月工资高于上年度社平三倍的,按社平三倍支付,向其支付经济补偿的年限最高不超过十二年。2008年之前,对于两种特定情形即——协商一致解除劳动合同劳动者不胜任向其补偿的08年之前的工作年限受12年封顶。经济补偿金中的N代表了什么?N、N+1、2N都是实践中约定俗成的一些叫法。以N为例,结合劳动合同法的相关规定,N是工作年限,用来指代劳动合同法中的经济补偿。N的适用范围有哪些?正常情况下,经济补偿就是N。解除劳动合同的经济补偿情形,主要有4种——用人单位提出的协商一致解除用人单位存在过错的劳动者辞职劳动者不存在过错的用人单位解雇经济性裁员终止劳动合同的经济补偿情形,主要有2种——除用人单位维持或者提高劳动合同约定条件续订劳动合同,劳动者不同意续订的情形外,劳动合同到期终止的;用人单位主体灭失(如用人单位被依法宣告破产的;用人单位决定提前解散的等)。N+1指的是什么?只有出现这3种情形——医疗期满、工作不胜任、客观情况发生重大变化,也就是上面提到的“劳动者不存在过错的用人单位解雇”,并且用人单位没有提前一个月通知,才需要支付N+1。这里的1,其实就是代通知金。2N指的是什么?2N就是违法解除劳动合同后,用人单位支付赔偿金。若用人单位违法解除,劳动者有以下选择——要求恢复劳动关系,继续履行劳动合同;或者要求用人单位支付违法解除劳动合同赔偿金,也就是我们俗称的2N。在劳动者要求支付赔偿金,或者劳动者要求恢复劳动关系但实践中难以恢复时,用人单位要向劳动者支付赔偿金即2N。2n+1和2(n+1)有无法律依据?“2n+1”和“2(n+1)”两种方式,没有法律依据。上面提到的N、N+1、2N都有特定的适用情形,但是不能兼得。若合法解除,就应支付N或N+1;若违法解除,就面临2N的问题。但无论哪一种,上述金额都是不能兼得的,所以“2n+1”和“2(n+1)”是没有法律依据的。bool IsExitsLoop(slist *head){  slist *slow = head, *fast =  while ( fast && fast-&next )   {    slow = slow-&    fast = fast-&next-&    if ( slow == fast )
      break;  }  return !(fast == NULL || fast-&next == NULL);}
下面看看怎么找环的入口,当fast与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1&=n)。假设slow走了s步,则fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:2s = s + nrs= nr设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。a + x = nra + x = (n & 1)r +r = (n-1)r + L - aa = (n-1)r + (L & a & x)(L & a & x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点(从相遇点向后遍历循环回到入口点的距离),于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇点为环入口点,也即为两个链表的第一个相同节点。程序描述如下:
slist* FindLoopPort(slist *head)
slist *slow = head, *fast =
while ( fast && fast-&next )
slow = slow-&
fast = fast-&next-&
if ( slow == fast )
if (fast == NULL || fast-&next == NULL)
return NULL;
while (slow != fast)
slow = slow-&
fast = fast-&
这种解法似乎是非常犀利,逻辑推理很棒,但是这种解法的时间复杂度是怎么样的呢??slow每次前进一步,fast每次前进两步,这样的话遍历多少步会结束呢???(求人解释)
2.3 抓住要点
  不妨遍历每个链表保存最后一个节点,看看最后一个节点是否是同一个节点,这种情况时间复杂度是O(length1 + length2)。基本也不需要什么空间,似乎是一个不错的想法哦,那么怎么找到第一个相交节点呢?可以遍历的过程中记录链表的长度L1和L2(假设L1&L2)这是遍历找到第一个链表中的第L1 - L2节点,然后链表一从第L1-L2个节点开始遍历,链表二从第一个节点遍历,每次前进一步,直到找到第一个相同的节点,则可以认为两个链表存在相交节点,并且该点即为第一个相交节点(原来这里写错了,感谢Ider指出这个错误)。这种解法的时间复杂度也是线性的,但是如果两个链表长度相差不多时,时间复杂度还是不错的。
  到这里,我知道的几种典型的解法就说完了。欢迎大神们提供新的思路!!
3.问题扩展:(思考)
  baidu曾经出过这样的一个笔试题目,归根到底也是找到两个链表是否存在相同的节点,但是数据量很大,即链表长度是上亿的。想想那么应该怎么处理呢?
            学习中的一点总结,欢迎拍砖哈^^
阅读(...) 评论()这里的dp范做根据网上的动态归法分析和网上的有个100个dp方程做的,题解很多是原版,没怎么动,有些是别人的一些其他做法,还有一些自己的想法。如果看到题解很别人一样,那就是摘自别人的。且这里只是一半。
由于本文有些摘自网上,如有原主看到不想在此贴出的,请说明,将会撤出。
如此文方法错误,或者冒犯某些原博主的文章还请见谅,还请指出,非常感谢
一、问题描述
总公司拥有高效生产设备M台,准备分给下属的N个公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中M《=15,N〈=10。分配原则:每个公司有权获得任意数目的设备,但总台数不得超过总设备数M。保存数据的文件名从键盘输入。
数据文件格式为:第一行保存两个数,第一个数是设备台数M,第二个数是分公司数N。接下来是一个M*N的矩阵,表明了第I个公司分配J台机器的盈利。
&&&& 这是一个典型的动态规划试题。用机器数来做状态,数组F[I,J]表示前I个公司分配J台机器的最大盈利。则状态转移方程为
F[I,J]=Max{F[I-1,K] + Value[I,J-K]} (0〈=K〈=J〉
背包变形tyvj1089smrtfun
一、问题描述
现有N个物品,第i个物品有两个属性A_i和B_i。在其中选取若干个物品,使得sum{A_i&+&B_i}最大,同时sum{A_i},sum{B_i}均非负(sum{}表示求和)。
输入:第一行,一个整数,表示物品个数N。&接下来N行,每行两个整数,表示A_i和B_i。
输出:一个整数,表示最大的sum{A_i&+&B_i}。
&N&&=&100&,&|A_i|&&=&1000&,&|B_i|&&=&1000
首先就是表示状态&f[i]表示当sum{b}为i时sum{a}的最大值,那么sum{b}就相当于背包的体积(可以为负),b[i]就相当于物品的体积(但可以为负值),那没有一个确定的背包体积怎么办呢。。就在线的扩展MAX,初始化MAX=0;比如说第一个b[i];放进去的时候就扩展一下MAX+=b[i];把Ai的和当背包的重量,把Bi的和当成价值来做,每次重量考察Ai和的最小到最大
对于f[]的上限注意了,可不是经典的零。。因为体积(sum{b[i]})可以为负所以就要设置一个MIN来表示其上限。不断地更新MIN,类似MAX那样更新。
最后把j从0到MAX枚举一下
对于当w[i]&0的时候为什么要把for循环那个MIN to&MAX+w[i]而不是AX&to&MIN+w[i];疑问:MIN to&MAX+w[i]不就是放无数次,成为完全背包了吗?答:因为w[i]&0.
f[j]:=max(f[j], f[j-a[i]]+b[i])
一、问题描述
设有整数序列b1,b2,b3,…,bm,若存在i1&i2&i3&…&in,且bi1&bi2&bi3&…&bin,则称 b1,b2,b3,…,bm中有长度为n的不下降序列bi1,bi2,bi3,…,bin。求序列b1,b2,b3,…,bm中所有长度(n)最大不下降子序列
输入:整数序列
输出:最大长度n和所有长度为n的序列个数Total
LIS(Longest Increasing Subsequence)最长上升(不下降)子序列,有两种算法复杂度为O(n*logn)和O(n^2)。在上述算法中,若使用朴素的顺序查找在D1..Dlen查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的时间复杂度为O(n^2),与原来算法相比没有任何进步。但是由于D的特点(2),在D中查找时,可以使用二分查找高效地完成,则整个算法时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D在算法结束后记录的并不是一个符合题意的最长上升子序列!算法还可以扩展到整个最长子序列系列问题。有两种算法复杂度为O(n*logn)和O(n^2)
O(n^2)算法分析如下
(a[1]...a[n] 存的都是输入的数)
  1、对于a[n]来说,由于它是最后一个数,所以当从a[n]开始查找时,只存在长度为1的不下降子序列;
2、若从a[n-1]开始查找,则存在下面的两种可能性:
(1)若a[n-1] & a[n] 则存在长度为2的不下降子序列 a[n-1],a[n].
(2)若a[n-1] & a[n] 则存在长度为1的不下降子序列 a[n-1]或者a[n]。
3、一般若从a[t]开始,此时最长不下降子序列应该是按下列方法求出的:
在a[t+1],a[t+2],...a[n]中,找出一个比a[t]大的且最长的不下降子序列,作为它的后继。
4、为算法上的需要,定义一个数组:
d:array [1..n,1..3]
d[t,1]表示a[t]
d[t,2]表示从i位置到达n的最长不下降子序列的长度
d[t,3]表示从i位置开始最长不下降子序列的下一个位置
最长不下降子序列的O(n*logn)算法
先回顾经典的O(n^2)的动态规划算法,设A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设F[t] = 0(t = 1, 2,..., len(A))。则有动态规划方程:F[t]= max{1, F[j] + 1} (j = 1, 2, ..., t - 1, 且A[j] & A[t])。
现在,我们仔细考虑计算F[t]时的情况。假设有两个元素A[x]和A[y],满足
(1)x & y & t (2)A[x] & A[y] & A[t] (3)F[x] =F[y]
此时,选择F[x]和选择F[y]都可以得到同样的F[t]值,那么,在最长上升子序列的这个位置中,应该选择A[x]还是应该选择A[y]呢?
很明显,选择A[x]比选择A[y]要好。因为由于条件(2),在A[x+1] ... A[t-1]这一段中,如果存在A[z],A[x] & A[z] & a[y],则与选择A[y]相比,将会得到更长的上升子序列。
再根据条件(3),我们会得到一个启示:根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满足F[t] = k的所有A[t]中的最小值。设D[k]记录这个值,即D[k] = min{A[t]} (F[t] = k)。
注意到D[]的两个特点:
(1) D[k]的值是在整个计算过程中是单调不上升的。
(2) D[]的值是有序的,即D[1]& D[2] & D[3] & ... & D[n]。
利用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断A[t]与D[len]。若A[t] & D[len],则将A[t]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = A[t];否则,在D[1]..D[len]中,找到最大的j,满足D[j] & A[t]。令k = j + 1,则有D[j] & A[t] &= D[k],将A[t]接在D[j]后将得到一个更长的上升子序列,同时更新D[k] = A[t]。最后,len即为所要求的最长上升子序列的长度。
在上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列!
这个算法还可以扩展到整个最长子序列系列问题,整个算法的难点在于二分查找的设计,需要非常小心注意。
最长上升子序列LIS算法实现& 最长上升子序列问题是各类信息学竞赛中的常见题型,也常常用来做介绍动态规划算法的引例,笔者接下来将会对POJ上出现过的这类题目做一个总结,并介绍解决LIS问题的两个常用算法(n^2)和(nlogn).
问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7....an,求它的一个子序列(设为s1,s2,...sn),使得这个子序列满足这样的性质,s1&s2&s3&...&sn并且这个子序列的长度最长。输出这个最长的长度。(为了简化该类问题,我们将诸如最长下降子序列及最长不上升子序列等问题都看成同一个问题,其实仔细思考就会发现,这其实只是&符号定义上的问题,并不影响问题的实质)
例如有一个序列:1 7 35 9 4 8,它的最长上升子序列就是 13 4 8 长度为4.
算法1(n^2):我们依次遍历整个序列,每一次求出从第一个数到当前这个数的最长上升子序列,直至遍历到最后一个数字为止,然后再取dp数组里最大的那个即为整个序列的最长上升子序列。我们用dp[i]来存放序列1-i的最长上升子序列的长度,那么dp[i]=max(dp[j])+1,(j∈[1, i-1]); 显然dp[1]=1,我们从i=2开始遍历后面的元素即可。
算法2(nlogn):维护一个一维数组c,并且这个数组是动态扩展的,初始大小为1,c[i]表示最长上升子序列长度是i的所有子串中末尾最小的那个数,根据这个数字,我们可以比较知道,只要当前考察的这个数比c[i]大,那么当前这个数一定能通过c[i]构成一个长度为i+1的上升子序列。当然我们希望在C数组中找一个尽量靠后的数字,这样我们得到的上升子串的长度最长,查找的时候使用二分搜索,这样时间复杂度便下降了。
一、问题描述
在一园形操场四周摆放N堆石子(N≤100),现要将石子有次序地合并成一堆.规定每次只能选相临的两堆合并成一堆,并将新的一堆的石子数,记为该次合并的得分.& & & & & &&
文件名由键盘输入,该文件内容为:
&&&&&& 第一行为石子堆数N;
&&&&&& 第二行为每堆石子数,每两个数之间用一空格分隔.
输出数据 :
&输出文件名为OUTPUT.TXT
&&&&& 从第1至第N行为得分最小的合并方案. 第N+1行为空行.从N+2到2N+1行是得分最大的合并方案.
&&&&& 每种合并方案用N行表示,其中第i行(1≤i≤N)表示第i次合并前各堆的石子数(依顺时钟次序输出,哪 一堆先输出均可). 要求将待合并的两堆石子数以相应的负数表示,以便识别,参见MODEL2.TXT
参考输入文件EXAMPLE2.TXT&&&&&&&&
参考输出文件MODEL2.TXT
看到本题,容易想到使用贪心法,即每次选取相邻最大或最小的两堆石子合并。
然而这样做对不对呢?看一个例子。
N=5&& 石子数分别为3 4 6 5 4 2。
用贪心法的合并过程如下:
第一次 3 4 65 4 2得分 5
第二次 5 4 65 4得分9
第三次 9 6 5 4得分9
第四次 9 6 9得分15
第五次 15 9得分24
然而仔细琢磨后,发现更好的方案:
第一次3 4 65 4 2得分 7
第二次7 6 54 2得分13
第三次13 5 4 2得分6
第四次13 5 6得分11
第五次 13 11得分24
显然,贪心法是错误的。
显然,贪心只能导致局部的最优,而局部最优并不导致全局最优。
仔细分析后,我们发现此题可用动态规划进行解决。
我们用data[I,j]表示将从第i颗石子开始的接下来j颗石子合并所得的分值,
max[i,j]表示将从第i颗石子开始的接下来j颗石子合并可能的最大值,那么:
max[I,j] = max(max[i, k] + max[i + k, j – k] + data[I,k] + data[I + k, j – k]) (2&=k&=j)
max[i,1] = 0
同样的,我们用min[i,j]表示将第从第i颗石子开始的接下来j颗石子合并所得的最小值,可以得到类似的方程:
min[I,j] = min(min[i, k] + min[i + k, j – k] + data[I,k] + data[I + k, j – k]) (0&=k&=j)
min[I,0] = 0
这样,我们完美地解决了这道题。空间复杂度为O(n2),时间复杂度也是O(n2)。
动态规划思路:
&&&阶段i:石子的每一次合并过程,先两两合并,再三三合并,...最后N堆合并
&&&状态s:每一阶段中各个不同合并方法的石子合并总得分。
&&&决策:把当前阶段的合并方法细分成前一阶段已计算出的方法,选择其中的最优方案
&&&具体来说我们应该定义一个数组s[i,j]用来表示合并方法,i表示从编号为i的石头开始合并,j表示从i开始数j堆进行合并,s[i,j]为合并的最优得分。
&&&对于上面的例子来说,初始阶段就是s[1,1],s[2,1],s[3,1],s[4,1],s[5,1],s[6,1],因为一开始还没有合并,所以这些值应该全部为0。
&&&第二阶段:两两合并过程如下,其中sum(i,j)表示从i开始数j个数的和
&&&&&&&&&&&&& s[1,2]=s[1,1]+s[2,1]+sum(1,2)
&&&&&&&&&&&&& s[2,2]=s[2,1]+s[3,1]+sum(2,2)
&&&&&&&&&&&&& s[3,2]=s[3,1]+s[4,1]+sum(3,2)
&&&&&&&&&&&&& s[4,2]=s[4,1]+s[5,1]+sum(4,2)
&&&&&&&&&&&&& s[5,2]=s[5,1]+s[6,1]+sum(5,2)
&&&&&&&&&&&&& s[6,2]=s[6,1]+s[1,1]+sum(6,2)
第三阶段:三三合并可以拆成两两合并,拆分方法有两种,前两个为一组或后两个为一组
&&&&&&&& s[1,3]=s[1,2]+s[3,1]+sum(1,3)或s[1,3]=s[1,1]+s[2,2]+sum(1,3),取其最优
&&&&&&&& s[2,3]=s[2,2]+s[4,1]+sum(2,3)或s[1,3]=s[2,1]+s[3,2]+sum(2,3),取其最优
&&&&&&&&&&&&&&&&&&&&&&&&&&&& ...
第四阶段:四四合并的拆分方法用三种,同理求出三种分法的得分,取其最优即可。以后第五阶段、第六阶段依次类推,最后在第六阶段中找出最优答案即可
一、试题描述
输入文件:第一行 顶点数N
&&&&&&&&& 第二行 N个顶点(从1到N)的权值
输出格式:最小的和的值
&&&&&&&&& 各三角形组成的方式
输入示例:5
121& 122& 123& 245&231
输出示例:The&minimum& is :
&&&&&&&&& The& formation&of 3 triangle:
&&&&&&&&& 3 4 5, 15 3, 1 2 3
二、试题分析
这是一道很典型的动态规划问题。设F[I,J](I&J)表示从顶点I到顶点J的凸多边形三角剖分后所得到的最大乘积,我们可以得到下面的动态转移方程:
F[I,J]=Min{F[I,K]+F[K,J]+S[I]*S[J]*S[K]}&&&& (I&K&J)
目标状态为:F[1,N]
但我们可以发现,由于这里为乘积之和,在输入数据较大时有可能超过长整形甚至实形的范围,所以我们还需用高精度计算,但这是大家的基本功,程序中就没有写了,请读者自行完成。
一道题目:设有一个长度N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:
有一个数字串: 312,当N=3,K=1时会有以下两种分法:
1)3*12=36
2)31*2=62
这时,符合题目要求的结果是: 31*2=62
输入:程序的输入共有两行:
第一行共有2个自然数N,K(6&=n&=40,1&=k&=6)
第二行是一个K度为N的数字串。
输出:结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。
设字符串长度为n,乘号数为k,如果n=50,k=1时,有(n-1)=49种不同的乘法,当k=2时,有C(2,50-1)=1176种乘法,既C(k,n-1)种乘法,当n、k稍微大一些的时候,用穷举的方法就不行了。
&&&设数字字符串为a1a2…an
&&&K=1 时:一个乘号可以插在a1a2…an中的n-1个位置,这样就得到n-1个子串的乘积:
&&&&& a1*a2…an, a1a2*a3…an, …, a1a2…a n-1*an( 这相当于是穷举的方法)
&&&此时的最大值= max{a1*a2…an,a1a2*a3…an, …, a1a2…a n-1*an }
&&&&& K=2时,二个乘号可以插在a1a2…an中n-1个位置的任两个地方, 把这些乘积分个类,便于观察规律:
&&&①最后一个数作为被乘数:
&&a1a2 …*a n-1*an , a1a2 …*a n-2 a n-1*an , a1*a2 …a n-3 a n-2 a n-1*an
&&设符号F[n-1,1]为在前n-1个数中插入一个乘号的最大值,则的最大值为F[n-1,1]*an
&&&②最后2个数作为被乘数:
&&a1a2 …*a n-2*a n-1an , a1a2 …*a n-3 a n-2* an-1 an , a1*a2 …an-3 a n-2*an-1an
设符号F[n-2,1]为在前n-2个数中插入一个乘号的最大值,则的最大值为F[n-2,1]*an-1an
&&&③最后3个数作为被乘数:
设符号F[n-3,1]为在前n-3个数中插入一个乘号的最大值,则最大值为F[n-3,1]+an-2an-an
&&a3~an作为被乘数:F[2,1]*a3 …an-2an-1an
&&此时的最大乘积为:
F[n,k]=max{F[n-1]*an,F[n-2,1]*an-1an,F[n-3,1]*an-2an-1an,F[2,1]*a3…an-2an-1an}
&&设F[i,j]表示在 i 个数中插入 j 个乘号的最大值,g[i,j]表示从ai到aj的数字列,则可得到动规方程:
F[I,j]= max{F[I-1,j-1]*g[I,I],F[I-2,j-1]*g[I-1,I],F[I-3,j-1]*g[I-2,I],
…,F[j,j-1]*g[j+1,I]}(1&=I&=n, 1&=j&=k)
&&边界: F[I,0] =g[1,I] (数列本身)
&&阶段:子问题是在子串中插入j-1,j-2……1,0个乘号,因此乘号个数作为阶段的划分(j个阶段)
&&状态:每个阶段随着被乘数数列的变化划分状态。
&&决策:在每个阶段的每种状态中做出决策。
&&还有2个问题需要注意:
&(1)输入的字符需要进行数值转换
&(2)由于乘积可能很大,所以可以使用大数类型
一、问题描述:
给定一些系统备用件的单价Ck,以及当用Mk个此备用件时部件的正常工作概率Pk(Mk),总费用上限C。求系统可能的最高可靠性。
输入:第一行:n& C
第二行:C1& P1(0)& P1(1) … P1(X1) (0&=X1&=[C/Ck])
  &&&&&& …
第 n 行:Cn& Pn(0)&Pn(1) … Pn(Xn) (0&=Xn&=[C/Cn])
输出:最大可靠性的值
2& 20&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &&&& &&&&&&&&&&&&
3& 0.6&0.65& 0.7& 0.75&0.8& 0.85& 0.9
5& 0.7&0.75& 0.8& 0.8&0.9
输出:0.6375
二、算法分析
&&& 1.证明这个问题符合最优化原理。可以用反证法证明之。假设用money的资金购买了前I项备用件,得到了最高的系统可靠性,而且又存在如下情况:对于备用件I,设要买Ki个,则可用的资金还剩余money– Ci*Ki,用这些钱购买前(I-1)项备用件,如果存在一种前(I-1)种备用件的购买方案得到的系统可靠性比当前得到的要高,那么这个新的方案会使得整个前I项备用件组成的系统可靠性比原先的更高,与原假设矛盾。所以可以证明这个问题符合最优化原理。
2.证明这个问题符合无后效性原则。
3.综上所述,本题适合于用动态规划求解。
4.递推方程及边界条件:
&& F[I,money] :=max { F[I-1,money – C[I]*K[I] ] } (0&=K[I]&=C div Ci )
三、参考程序
(noip’2005)
一、&&&&&&&&&&&&&问题描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
  题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
  对于30%的数据,L &= 10000;
  对于全部的数据,L &= 10^9。
输入:第一行有一个正整数L(1&&=&L&&=&10^9),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1&&=&S&&=&T&&=&10,1&&=&M&&=&100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开
输出:输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。
1.分s=t和s&t考虑。当s=t时,只需要考虑第i个位置上有石子且i&mod&s=0的情况;
2.当s&t时,压缩桥长和石子位置(要考虑到石子位置不一定有序),若两石子间空白区&maxk+2*t,我们就把它缩短为前一个石子位置加上maxk+2*t(可证得maxk=100时满足所有区间);
3.从1到L(压缩后最后一个石子位置)+t-1进行动态规划,f[i]=min{f[i-j]+1(如果i处不为石子),f[i-j](如果i处为石子)}(j=s..t,f[i]初始值为100,最多有100颗石子);
4.答案就是min{f[L],f[L+1],...,f[L+t-1]}。
这题因为 1 &= L &= 10^9 L非常大,只能进行状态压缩。如果 L小到可以开数组,我们可以设一个数组Dp[L_MAX]保存每个子状态,从后往前DP,Dp[n] 表示从坐标位置 n 跳到或者跳过终点的最优解(局部最优),求Dp[n-1],Dp[n-1] = min{Dp(n-1+minJump ~ n-1+maxJump)},minJump表示青蛙最小的跳数,maxJump表示青蛙最大的跳数,如果 n-1 位置有石头则Dp[n-1]=Dp[n-1]+1.
比如对于示例:
首先我们初始化数组,Dp[]={0},从坐标位置7开始算,Dp[7]=min{Dp(2+7,3+7)}=0,因为7位置有石头所以Dp[7]=Dp[7]+1=1,同理,Dp[6]=1,Dp[5]=1,Dp[4] = min{Dp(4+2,4+3)} = 1,因为5位置没有石头所以Dp[4]不用加1,最后可求得 Dp[] = {2,1,2,2,1,1,1,1,0,0,0 ……},Dp[0]即为全局的最优解,表示从起点跳到或者跳过终点青蛙最少要踩到的石头数目。
这题因为L很大,子状态不能全部保存,我们观察到,当求Dp[n]的值时用到的状态只有
Dp(n+minJump,n+maxJump)所以我们可以不用全部保存每个坐标位置的Dp值,而只要保存从n位置之后最多maxJump个坐标的Dp值即可。为此我们可设Dp[maxJump](对本题可设Dp[10]),注意这里的下标不表示坐标位置,我们可用一个变量nHeadStart跟踪坐标位置,当求n位置的Dp值时,nHeadStart=n+1。到这里我们解决了空间问题,真正难理解的是下面的状态压缩。
假如测试数据是:
这个位置的Dp值我们知道是 1,而该位置前面有个位置都没有石头,我们不可能也没有时间去求所有这些中间位置的Dp值,但是10位置的值却与这中间的有关,HowCan I do ???假如有这样一组Dp值,Dp[]={25 1 3} 我们观察到单minJump != maxJump时,因为每次都取最小值所以经过有限步运算后,Dp={1 1 1 1},即所有的Dp值都变成开始计算时的最小值。变化经过如下
数据:minJump=2, maxJump=3,Dp[]={2,5,1,3}
Dp[]={1,2,5,1}
Dp[]={1,2,2,5}
Dp[]={2,1,2,2}
Dp[]={1,2,1,2}
Dp[]={1,1,2,1}
Dp[]={1,1,1,2}
Dp[]={1,1,1,1}
即从当前位置到前面 7 个及 7 个以前的空位(没有石头),我们断定Dp[]={1,1,1,1}
因此,对 2 6 5 10 ,我们可以知道从 11 位置开始,Dp[]={0,0,0,0,0}(只需保存maxJump个)前面的就好做了。经过几步的空位可以达到稳定状态(我称Dp数组值不再变化的状态为稳定态),我们有两种方法
其一:每求一个空位后我们求Dp数组中的最值,如果最大和最小值相等就达到稳定态了。这种方&&&&& 法因为每次都要求最值,因此当maxJump比较大的时候不适合。
其二:我们可以断定如果当前位置之前至少有maxJump*maxJump个空位,就一定会出现稳定态(请读者自己理解 这样我们遇到空位大于等于maxJump*maxJump时直接设Dp数组为最小值。
上面的是minJump != maxJump的情况,当minJump==maxJump时,假如Dp[]={0,0,1}
数据:minJump=3, maxJump=3,Dp[]={0,0,1},Dp的变化情况如下:
Dp[]={1,0,0}
Dp[]={0,1,0}
Dp[]={0,0,1}
出现重复的情况,且min{Dp[]} != max{Dp[]},因此不能按minJump !=maxJump的情况处理。这种情况只要将重复出现的位去掉即可,比如上面的如果当前位置前有& 4 个空位,前三个不用计算,因为到第三个位置的Dp值和当前的Dp值一样。
Dp[]数组的下标变化大家可以参考循环队列的情况。
关于压缩空长条步数的证明
对于步幅s到t 若目标位置距起始点距离D≥s×(s+1)则一定可以到达目标点
设一次可以走p步或p+1 方便起见,我们取起始位置为坐标0点
那么p×(p-1)点一定可以达到(每次走p的距离,走p-1次)
因为我们也可以每次走p+1步
所以可以通过将一次p距离的行走替换为p+1距离的行走来实现总距离+1
比如说我们要达到p×(p-1)+1点我们只需要走p-2次p的距离和一次p+1的距离即可到达
我们整理两个表达式p×(p-1)+1和(p-2)×p+(p+1)就可证明上述正确
现在我们从(p-1)×p位置开始逐一推算可行性
(p-1)×p+1=(p-2)×p+(p+1)
(p-1)×p+2=(p-3)×p+(p+1)×2
(p-1)×p+3=(p-4)×p+(p+1)×3
(p-1)×p+(p-1)=(p-p)×p+(p+1)(p-1)
我们已经用光了所有可以替换的p距离行走。
那么(p-1)×p+p如何实现呢?
对上面多项式整理得:p×p
显然我们只需要进行p次p距离的行走就可以到达目标
也就是说我们通过用p+1代换p的方法前进p-1步
在前进第p步时重新整合成关于p的多项式(一定是p的倍数)如此往复。
而我们要前进p-1步至少需要p-1个p。所以下限为p×(p-1)
至此,命题得证。
(noip’2003)
一、问题描述
设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数
若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;
1)tree的最高加分
2)tree的前序遍历
输入格式 InputFormat
&&&第1行:一个整数n(n<30),为节点个数。
第2行:n个用空格隔开的整数,为每个节点的分数(分数<100)。
输出格式 OutputFormat
&&&第1行:一个整数,为最高加分(结果不会超过4,000,000,000)。
第2行:n个用空格隔开的整数,为该树的前序遍历。
样例输入:5
5 7 1 2 10
样例输出:5
5 7 1 2 10&&&&&&&&&&&&&&&&&&
二、分析:
用f[i,j]表示合并[i,j]这段区间的最大加分,k为根,则有
&f[i,j]:=max(f[i,k]*f[k,j]+score[k]);{i&=k&=j}
边界是空树的加分为1,单位区间的最大加分为本身的加分。
目标为f[1,n];
由于要输出方案,所以在转移时记录转移的k.最后递归输出即可。
注意就是先循环长度,再枚举开始位置,用长度和开始位置求出结束位置之后再枚举k。因为转移的时候,要求比当前区间短的区间都要知道。
------------------
先画画这种树算算样例
发现每个树划分为左右子数后 左右子树也是按照同样的方法划分 形成了重叠子结构
马上想到区间DP 配合四边形不等式O(N*N)
一、问题描述
&& 大学里实行学分。每门课程都有一定的学分,学生只要选修了这门课并考核通过就能获得相应的学分。学生最后的学分是他选修的各门课的学分的总和。
每个学生都要选择规定数量的课程。其中有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如,《数据结构》必须在选修了《高级语言程序设计》之后才能选修。我们称《高级语言程序设计》是《数据结构》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。为便于表述每门课都有一个课号,课号依次为1,2,3,……。下面举例说明
&&& 上例中1是2的先修课,即如果要选修2,则1必定已被选过。同样,如果要选修3,那么1和2都一定已被选修过。
&&& 学生不可能学完大学所开设的所有课程,因此必须在入学时选定自己要学的课程。每个学生可选课程的总数是给定的。现在请你找出一种选课方案,使得你能得到学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。
&&& 输入文件的第一行包括两个正整数M、N(中间用一个空格隔开)其中M表示待选课程总数(1≤M≤1000),N表示学生可以选的课程总数(1≤N≤M)。
&&& 以下M行每行代表一门课,课号依次为1,2……M。每行有两个数(用一个空格隔开),第一个数为这门课的先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。学分是不超过10的正整数。
&&& 输出文件第一行只有一个数,即实际所选课程的学分总数。以下N行每行有一个数,表示学生所选课程的课号。
本题看上去不太好动手。这是一道求最优解的问题,如果用搜索解题,那规模未免过于庞大;用动态规划,本题数据之间的关系是树形,和我们往常见到线性的数据关系不一样。
怎么办?我们先从一些简单的数据入手分析。如表1所示的输入数据,我们可将它看为由两棵树组成的森林,如图1所示。
我们添加一个顶点0,并且在每棵树的顶点与0之间连一条边使森林成为一棵树,如图2。
我们发现,我们可以选取某一个点k的条件只是它的父节点已经被选取或者它自己为根节点;而且我们不论如何取k的子孙节点,都不会影响到它父节点的选取情况,这满足无后效性原则。于是我们猜测,是不是可以以节点为阶段,进行动态规划呢?
我们用函数f(I,j)表示以第i个节点为父节点,取j个子节点的最佳代价,则:
可是如此规划,其效率与搜索毫无差别,虽然我们可以再次用动态规划来使它的复杂度变为平方级,但显得过于麻烦。
我们继续将树改造:原本是多叉树,我们将它转变为二叉树。如果两节点a,b同为兄弟,则将b设为a的右节点;如果节点b是节点a的儿子,则将节点b设为节点a的左节点。树改造完成后如图3。
我们用函数f(I,j)表示以第i个节点为父节点,取j个子节点的最佳代价,这和前一个函数表示的意义是一致的,但方程有了很大的改变:
这个方程的时间复杂度最大为n3,算十分优秀了。
在具体实现这道题时,我们可以自顶而下,用递归进行树的遍历求解;在空间方面,必须特别注意。因为如果保存每一个f(I,j),bp下是不可能的。我们必须用多少开多少,这样刚好可以过关。(具体请参见程序)
分析(自)
很容易想到在树上背包来解决问题,假设f[i][j]为以i为根的子树,包括i,选择j门课的最大值
那么有f[i][j]=max{ f[i][a]+f[k][b] }k是i的子树,a+b=j
这样的复杂度是O(n*m^2)对于这题的数据范围来说还是够用的,不过我用上了对这种泛化物品的背包的一种优化复杂度降为O(n*m)
(noip’96)
一、&&&&&&&&&&&&&问题描述
设有1g、2g、3g、5g、10g、20g的砝码各若干枚(其总重&=1000),用他们能称出的重量的种类数。
输入:a1 a2 a3 a4 a5 a6表示1g砝码有a1个,2g砝码有a2个,…,20g砝码有a6个,中间有空格)。
输出:Total=N(N表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的情况)。
样例输入 1 1 0 0 0 0
样例输出 3
就是个01背包……f[0]:=true把问题稍做一个改动,已知a1+a2+a3+a4+a5+a6个砝码的重量w[i], w[i]∈{ 1,2,3,5,10,20} 其中砝码重量可以相等,求用这些砝码可称出的不同重量的个数。这样一改就是经典的0/1背包问题的简化版了,求解方法完全和上面说的一样
设dp[1000]数组为标记数组。当dp[i]=0时,表示质量为i的情况,目前没有称出;当dp[i]=1时,表示质量为i的情况已经称出。题目中有多个砝码,我们顺序处理每一个砝码。当处理第j个砝码,质量为wj时,有下列推导公式:
d[i-wj]=1==&d[i]=1(当且仅当质量i-wj已经称出,才能称出质量i)
一、题目描述
一个核电站有N个放核物质的坑,坑排列在一条直线上。如果连续M个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质。
现在,请你计算:对于给定的N和M,求不发生爆炸的放置核物质的方案总数。
法1:设f[i]为第i个位置不爆炸的方案数,注意!这个f一定是合法的!则这个位置可以放/不放,(别急,不一定合法~)f[i-1]*2但不能连续放m个,所以&=m时需要减去保证只连了m个,即前m-1全放了,而i-m(绿圈)一定不放 的 1种情况
----f[i-m-1]&---(合法的) 不管它是如何组成的,这是dp的精髓正对的头是i-m+1
而初始值f[0]=1有意义,第0个就是不放。但f[-1]是由 推f[m]=f[m-1]*2-f[i(m)-m-1]为了正解,设的f[-1]=1
(noip2001)
一、问题描述
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5; 1,5,1; 5,1,1;
问有多少种不同的分法。
输入:n,k(6&n&=200,2&=k&=6)
输出不同分法
例如:7 3 输出4
分析:用f(I,j)表示将整数I分成j分的分法,可以划分为两类:
& 第一类 :j分中不包含1的分法,为保证每份都&=2,可以先那出j个1分到每一份,然后再把剩下的I-j分成j份即可,分法有:f(I-j,j).
& 第二类 : j份中至少有一份为1的分法,可以先那出一个1作为单独的1份,剩下的I-1再分成j-1份即可,分法有:f(I-1,j-1).
所以:f(I,j)=f(I-j,j)+ f(I-1,j-1)
一、题目描述
给定一个n*m的01矩阵
如果在一个矩形中,边框上的数都是1,其余数都是0,那么我们称这个矩形为0类矩形
Your Task:求出面积最大的0类矩形的面积s
输入:第一行两个数n、m。接下来n行每行m个数。n &= 2000,m &= 2000
输出:每行一个数s
本题中符合要求的矩形可以分为2种:
1.1*k和2*k
2.k1 * k2的
对于第一种,由于这种矩形全部是由1构成的,扫描一次更新答案就可以了。
对于第二种,注意到里面那层0构成0的一个极大联通块。因此,我们只用找出每一个0的极大联通块,然后判断是否是题目要求的图形就可以了。
在判断的时候,只用注意3个地方:
1.是否有0延伸到了矩阵的边缘,有的话显然不符合要求
2.这个联通块是否构成一个矩形。构不成矩形显然也不行。
3.周围的1是否能构成一个完整的壳。
具体实现的时候,可以记录这个极大联通块所能延伸到最远的行列。即程序中的max_row,min_row,max_column,min_column。以及当前块的面积sum.
然后对1,只用判断是这四个值是否等于对应的边界。
对于2,只用判断又这四个行列所确定的矩形面积是否等于sum。
对于3,只用判断加上1之后的大矩形四个角是为1,因为边是一定全部为1的。
这样,就可以在O(mn)的时间内解决本题了。
(Scoi’05)
一、问题描述
这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和
最大。注意:选出的k个子矩阵不能相互重叠。
输入格式:第一行为n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下来n行描述矩阵每行中的每个元素的分值(每个元素的分值的绝对值不超过32767)。
输出格式:只有一行为k个子矩阵分值之和最大为多少
样例输入:
由于m&=2可以分情况讨论
(1)m = 1 时就相当于1维,用g[i][j]表示前i个数字选出j段的最大分值转移是O(N)的,往前枚举即可
(2)m = 2时f[i][j][k]表示第一列前i个数字,第二列前j个数字选出k个子矩阵的最大分值转移还是O(N)
f[i][j][k] =max(f[i - 1][j][k], f[i][j - 1][k]);
f[i][j][k] =max{ f[i][j][k], f[x][j][k - 1] + s1[i] - s1[x] };
f[i][j][k] =max{ f[i][j][k], f[i][y][k - 1] + s2[j] - s2[y] };
当 i = j 时 f[i][j][k] = max{ f[i][j][k], f[x][x][k - 1] + s1[i] - s1[x] +s2[i] - s2[x] }
如果是 2*n 的话用 a1 表示第一行的个数,a2表示第二行,k表示选出矩阵个数DP情况是靠最后一个矩阵是否在边界上。如果a1 != a2 那么dp(k,a1,a2)& = max(dp(k,a1-1,a2),dp(k,a1,a2-1),dp(k-1,a1-j,a2)+ value(第k矩阵),dp(k-1,a1,a2-j) +value(第k矩阵)),a1 == a2,那么就有可能是最后一个矩阵是一个2*j的矩阵。那么就还有可能最大的是这种情况那么就加dp(k,a1,a2)&
= max(dp(k,a1,a2),dp(k-1,a1-j,a2-j) + value(第k矩阵))
+-被k整除(poj1745)
一、题目描述:n个整数中间填上+或者-,运算结果能否被k整除。1&=n&=10000,2&=k&=100
二、分析:已知(a+b)%m =(a%m + b%m)%m;那么f[i,j] 表示 前i个数运算结果mod k是否存在余数j,转移方程就简单了:如果f( i-1, j)为true,那么把f(i,(j+a[i])mod k)和f(i, (j-a[i])mod k)置真。由于可能会出现复数的情况,处理可以要么把数组开到负的,要么加一个很大的k的倍数再mod。要么特判
一、问题描述:一款叫做方块消除的游戏。游戏规则如下:n个带颜色方格排成一列,相同颜色的方块连成一个区域(如果两个相邻方块颜色相同,则这两个方块属于同一区域。游戏时,你可以任选一个区域消去。设这个区域包含的方块数为x,则将得到x^2个分值。方块消去之后,其余的方块就会竖直落到底部或其他方块上。而且当有一列方块被完全消去时,其右边的所有方块就会向左移一格。虽然游戏很简单,但是要拿高分也很不容易。找出得最高分的最佳方案
输入格式:第一行包含一个整数n(0&=n&=100),表示方块数目。第二行包含n个数,表示每个方块的颜色(1到n之间的整数)。
输出格式:仅一个整数,即最高可能得分。
/*设计状态f[i,j,k]表示[i..j]段序列和后面的k个方块消去所得到的最大值。说明:
后面的k个方块是火星来的咩?肯定不是...是后面的某个区间有k个方块,暂且称之为late。此区间的颜色和j区间相同。消掉了j和late区间中间的方块,于是j区间和late区间就连在一起了,并且消掉[j+1..late-1]的这一块的值是在上一步子问题中解决的,在这一个子问题中体现不出来。
刚开始看别人的解释也是一头雾水,后来看了程序就明白了。
子问题是一段区间的序列怎么消,和别人一起消,还是自己消了算了。
len[late]=k;colour[j]=colour[late];现在late和j紧挨在一起,可以算是一个区间了。
状态转移:
f[i,j,k]:=f[i,j-1,0]+(len[j]+k)^2;如果现在就将j和late的和区间消掉。
f[i,j,k]:=f[i,p,len[j]+k]+f[p+1,j-1,0];(colour[p]=colour[j]=colour[late])如果j和late再和前面的某一区间合并后再消掉。从这里也可以看出为什么求解f[i,p,len[j]+k]这一个子问题的时候没有求到f[p+1,j-1,0]这一部分,这解释了上一部分中的斜体字部分。
f[i,j,k]:=(len[i]+k)*(len[i]+k);(i=j);
用f [ i ] [ j ] [ k] 表示把(color [ i ] , len [ i ] ), (color [ i+1 ] , len [ i+1 ] ) ,……, (color [j-1 ] , len [ j-1 ] ),(color [j ] , len [ j ]+k )合并的最大得分
考虑(color [ j ] ,len [ j ]+k )这一段要不马上消去 要不和前面的一起消去
如果马上消去:f【i】【j-1】【0】+(len【j】+k)^2
如果和前面一起消去 那么前面有的段数可以有P位 那么得分可以有P种情况
f【i】【p】【k+len[j]】+f【p+1】【j-1】【0】;(color【p】=color【j】& i&=p&j)
所以f 【i】【j】【k】=两个的最大值. 边界条件是f【i】【i-1】【0】=0*/
(noip2001)
一、问题描述:有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30=,每个物品有一个体积(正整数)。要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
二、分析:首先,对于第I个背包,有选与不选两种状态。我们设F[i]表示使用容量为i的背包所获得的最大价值,则F[i]:=Max(F[i-w[i]]+a[i],F[i])这个状态转移方程
示出了一个数字三角形。 请编一个程序计算从顶至底的某处的一条路径,使该路径所经过的数字的总和最大。每一步可沿左斜线向下或右斜线向下走;1&三角形行数&25;
三角形中的数字为整数&1000;
一、问题描述:晴天小猪来到了一座深山的山脚下,因为只有这座深山中的一位隐者才知道这种药草的所在。但是上山的路错综复杂,由于小小猪的病情,晴天小猪想找一条需时最少的路到达山顶,山用一个三角形表示,从山顶依次向下有1段、2段、3段等山路,每一段用一个数字T(1&=T&=100)表示,代表晴天小猪在这一段山路上需要爬的时间,每一次它都可以朝左、右、左上、右上四个方向走(注意:在任意一层的第一段也可以走到本层的最后一段或上一层的最后一段)。晴天小猪从山的左下角出发,目的地为山顶,即隐者的小屋。
输入格式:第一行有一个数n(2&=n&=1000),表示山的高度。从第二行至第n+1行,第i+1行有i个数,每个数表示晴天小猪在这一段山路上需要爬的时间。
输出格式:一个数,即晴天小猪所需要的最短时间。
DP数字三角形的一个扩展,一道好题。可得dp方程
f[i,j]=min{f[i-1,j-1],f[i-1,j],f[i,j-1],f[i,j+1]}+a[i,j]
边缘的走法:
(5, 1)能走到(5, 5)(5, 2)(4, 1)(4, 4)
(5, 5)能走到(5, 1)(5, 4)(4, 1)(4, 4)
重诉一下题目的意思,第一小猪走的方向有四个左上,右上,左,右,在任意一层的第一段也可以走到本层的最后一段或上一层的最后一段,而最后一段也可以走到第一段。找从左下角走到山顶的最少时间。第二数据给出的是圆锥形的。
与数字三角形不同的地方就是可以左,右走,在任意一层的第一段也可以走到本层的最后一段或上一层的最后一段,而最后一段也可以走到第一段。第一段和最后一段的特殊性可以单独判断,重点在于如何去掉左右走的后效性,和找出当前的最小值。这里说一种简单的方法,网上大多数的人都用这种方法也做这道题。就是先进 行左上和右上的操作这样就一定有一个已经确定了的最小值,它在左,右操作都不会被改变,因为下面一层已经计算出最优值,它一定是由那下面直接走上来的。然后由这个点去改变其他点,然后判断出第一个和最后一个的值,是否可以再次被改变。再从头到尾循环一次,从尾到头循环一次,这样就可以计算出当前一层的最优
值,这样一值做到第一层。
网上的题解:
1.DP有环怎么办?
别急,先别想着放弃DP,有时候环是可以避免的.这里在每一行中为避免相邻两格左右移动产生的环,可以先推向左的,再推向右的,而同向移动产生的那个“大”环就麻烦一点.其实有个很简单的窍门:先记录从下一行转移来的最优值,然后在本行中寻找代价最小的点,以这个点为起点分别向左向右推,因为最小的点 显然是不需要从两侧的点过来的.这样就没有后效性了..
2.递推的顺序:
递推有两种顺序,可以根据当前状态值推出所有可能的后继状态,也可以根据所有当前状态可能的前驱来推当前值,很多时候,当问题的状态比较有规律时,这两种 方法是不相上下的.但是其他情况下一不小心就可能搞错.比如这题题目告诉我们的是从一个状态可行的所有走法(共四种),所以根据这个顺序去编是最保险的。 因为这里一个状态的前驱不一定只是四个,边缘的点是特例,可能会有5个来源,所以DP的时候不要随便换状态转移顺序.
这道题还可以用最短路经算法来做,把两个可以走的点看成一条边。
对于程序,可以自己画个图
(双向动态规划1,数字三角形)
问题描述:xuzhenyi要办个签证。办证处是一座M层的大楼,1&=M&=100。每层楼都有N个办公室,编号为1..N(1&=N&=500)。每个办公室有一个签证员。签证需要让第M层的某个签证员盖章才有效。每个签证员都要满足下面三个条件之一才会给xuzhenyi盖章:
1. 这个签证员在1楼
2. xuzhenyi的签证已经给这个签证员的正楼下(房间号相同)的签证员盖过章了。
3. xuzhenyi的签证已经给这个签证员的相邻房间(房间号相差1,楼层相同)的签证员盖过章了。
每个签证员盖章都要收取一定费用,这个费用不超过。找出费用最小的盖章路线,使签证生效
DP顺序:从下到上,再从左到右,再从右到左,与上题一样,为了保证其无后效性,所以同一层的需要分开扫描
坐标型DP。设计一个状态f[i,j]为找第i层第j个签证员所要花费的总费用。
状态转移很明显。
f[i,j]:=min{f[i-1,j]+a[i,j];f[i,j-1]+a[i,j];f[i,j+1]+a[i,j];}
只是f[i,j-1]和f[i,j+1]不能够在同一次循环中全部求出来,所以要扫两次。
记录路径:用x和y分别记录
一、问题描述:棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。棋盘用坐标表示,A点(0, 0)、B点(n, m)(n, m为不超过15的整数),同样马的位置坐标是需要给出的。现在要求你计算出卒从A点能够到达B点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
二、分析:就是简单考虑路径,但是这里需要注意的一个是马的地方不能走,这个特判下就好了,还有就是边界条件,如果马走了边界,那么边界要注意赋值为前一个
(tyvj’1505)
一、&&&&&&&&&&&&&问题描述:
在一个凹槽中放置了n层砖块、最上面的一层有n 块砖,从上到下每层依次减少一块砖。每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示。
14& 15&&4& 3& 23
& 33&33& 76& 2
&&& 2&&13& 11
&&&&& 22&23
&&&&&&& 31
如果你想敲掉第i层的第j块砖的话,若i=1,你可以直接敲掉它;若i&1,则你必须先敲掉第i-1层的第j和第j+1块砖。
你现在可以敲掉最多m块砖,求得分最多能有多少。
f[i,j,k]表示,到了第i行,总共取j个,在第i行取k个用g[i,j,k]数组记录从f[i,j,k]到f[i,j,i]中的最大值,用s[i,j]记录转换后的第i行前j个数的和
方程:f[i,j,k]=g[i-1,j-k,k-1]+s[i,k];
把三角形倒过来了!
  比如说样例:
    2 2 3 4
    8 2 7
    2 3
    49
  倒过来之后——&
    3 7
    2 2 3
    2 8 2 49
  那么每一点只能由左边的点转移过来,和左上角一直到上一行末尾的数转移过来。
如第四行第2个数可以从第三行的1,2,3个数转移过来。为什么?再回去看看题吧……
此题就是典型的区间DP,先搬砖块都变成直角三角形,然后分析阶段和边界,通过题里的叙述很容易推出递推公式:f[i][j][k]=f[i][j][k]+a[v][i];
边界:f[n+1][0][0]=0;
目标:f[i][j][k];
从右向左推,f[i,j,k]表示第i列敲掉j个砖头一共敲掉k个砖头所得到的最优值。第i列敲掉j个砖头,则第i+1列就至少要敲掉j-1个砖头。第i列一共要敲掉k个,第i+1列及其以前就要敲掉k-j个砖头。
f[i,j,k]:=max(f[i+1,v,k-j])+sum[j,i];(j-1&=v&=n-i)
sum[j,i]表示第i列,从第1行到第j行所有砖头的值的和
注意边界条件,赋初值要赋一个很大的负数。然后把f[n+1,0,0]赋成0
0&=j&=n-i+1,j&=k&=m
注意:j应该从0开始,而不能从1开始,因为该列可以一个也不取,而这个状态应该被保存,否则不一定达到最优。
(CscIIvijos 1441)
一、问题描述:阿Q编写了一个打鼹鼠的游戏:在一个n*n的网格中,在某些时刻鼹鼠会在某一个网格探出头来透透气。你可以控制一个机器人来打鼹鼠,如果i时刻鼹鼠在某个网格中出现,而机器人也处于同一网格的话,那么这个鼹鼠就会被机器人打死。而机器人每一时刻只能够移动一格或停留在原地不动。机器人的移动是指从当前所处的网格移向相邻的网格,即从坐标为(i,j)的网格移向(i-1, j),(i+1, j),(i,j-1),(i,j+1)四个网格,机器人不能走出整个n*n的网格。游戏开始时,你可以自由选定机器人的初始位置。现在你知道在一段时间内,鼹鼠出现的时间和地点,希望你编写一个程序使机器人在这一段时间内打死尽可能多的鼹鼠。
输入格式:文件第一行为n(n&=1000), m(m&=10000),其中m表示在这一段时间内出现的鼹鼠的个数,接下来的m行每行有三个数据time,x,y表示有一只鼹鼠在游戏开始后time个时刻,在第x行第y个网格里出现了一只鼹鼠。Time按递增的顺序给出。注意同一时刻可能出现多只鼹鼠,但同一时刻同一地点只可能出现多只鼹鼠。
输出格式:输出文件中仅包含一个正整数,表示被打死鼹鼠的最大数目。
二、分析:分析:经典DP题。这题一开始很容易想到一个(n^2t)的算法,定义dp[i][j][k]表示在第i秒,站在点(j,k)上所能打到的最多鼹鼠数目,方程也很好列。可惜n最多要有1000,而且k也不小,使用这个方法必定超时+超内存。仔细分析此题,发现假如机器人要移动到某个位置,那么它一定是走一条最短路径(其实就是曼哈顿距离,公式distance=|x1-x2|+|y1-y2|)否则解就不一定是最优的。还有,假如在某个点打死了一个地鼠,那么机器人的位置一定是站在那个死掉地鼠出现的位置上。这样我们就可以想出一个新的状态表示方法:定义dp[i]表示对于前i只鼹鼠,最多能打死多少只,而且当前机器人站在i号鼹鼠的位置。这样方程就类似于LCS问题,也就是对于当前的鼹鼠位置,枚举机器人上一次打死鼹鼠的地方,判断这两个地方是否可以到达,如果可以到达那么dp[i]+1,否则continue。
这里有一个小优化:在枚举鼹鼠位置的时候可以倒着枚举,这样简单的常数优化居然提高了1.2s的程序运行速度!
一、问题描述:传说中的九头龙是一种特别贪吃的动物。虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落。
有一天,有M个脑袋的九头龙看到一棵长有N个果子的果树,喜出望外,恨不得一口把它全部吃掉。可是必须照顾到每个头,因此它需要把N个果子分成M组,每组至少有一个果子,让每个头吃一组。
这M个脑袋中有一个最大,称为“大头”,是众头之首,它要吃掉恰好K个果子,而且K个果子中理所当然地应该包括唯一的一个最大的果子。果子由N-1根树枝连接起来,由于果树是一个整体,因此可以从任意一个果子出发沿着树枝“走到”任何一个其他的果子。
对于每段树枝,如果它所连接的两个果子需要由不同的头来吃掉,那么两个头会共同把树枝弄断而把果子分开;如果这两个果子是由同一个头来吃掉,那么这个头会懒得把它弄断而直接把果子连同树枝一起吃掉。当然,吃树枝并不是很舒服的,因此每段树枝都有一个吃下去的“难受值”,而九头龙的难受值就是所有头吃掉的树枝的“难受值”之和。
九头龙希望它的“难受值”尽量小,你能帮它算算吗?
题目简述:将一棵树中的节点染成M种颜色,每个节点有且只有一种颜色,在满足以下条件下使得两端颜色相同的边的权值和最小,所有边权均非负。(1)必须有K个1号颜色的点;(2)1号节点必须是1号颜色;(3)每种颜色必须至少有一个节点。如无解,输-1。
无解的情况很明显,当且仅当N-K&M-1时无解。有解的考虑用动态规划来解决。
如果以一棵子树作为一个子结构,分析需要考虑的状态:
(1)根节点的颜色。(2)1号颜色的个数。(3)树中颜色的分配情况,如何保证每种颜色都有节点。
初步分析可以得到一种四维的状态:
f[i][j][k][w],表示在以i为根的子树中,有j个1号节点,根染k号颜色,树中已有的颜色用w表示(w是一个二进制数)的状态下最小的权值和。
首先,这个方程用到了状态压缩w,因此对于本题300的数据范围是不现实的,需要继续思考。
假设这样一个问题,仍然是对树染色,可以任意染色,那么只要2种颜色,就可以保证任意一条边两端的颜色不同,联想到这道题,因为1号颜色比较特殊,因此单独处理,而余下的颜色如果大于等于2种,那么无论1号颜色如何染色,都可以保证一条边两边不会出现相同的非1号颜色的情况,换言之,如果M&=3,对答案有贡献的只有1号颜色节点之间的边。这样当M&=3时,可以直接按3处理,这样状态压缩是可以承受的。既然有了这样的优化,k也可以只用0,1来表示,1表示1号颜色,0表示非1号颜色。而M=2时就更简单了,0,1就直接把颜色分开了。
初步分析下来,得到了一个状态数为O(N*K*2*2?),转移为O(K*2*2?),总复杂度为O(N*K?*256)。由于N,K≤300,理论分析是会超时,但实际操作中可以不用循环到K,因为循环的上限可以设为min(K,子树i的总节点数)。这样的话,这个复杂度还是可以承受的。
本题还有优化吗?答案是肯定的。
如果要优化状态,前3维似乎是无法优化的,考虑第4维。之所以一开始要加入这一维,就是担心会存在有一些颜色无法染上的问题,经过后来的分析,发现除了1号颜色会对答案有贡献之外,其他颜色其实是可以被忽略的,因为我们可以保证它们不会对答案造成影响,那么只要有足够多的节点来染除1外的颜色,就可以确保每一种颜色都可以被染上,至于到底在哪里,其实并不重要。这样想,就会发现其实第四维是完全多余的,可以直接略去。
最终状态:f[i][j][k], 表示在以i为根的子树中,有j个1号节点,根染k号颜色的状态下最小的权值和。
如果能把M个脑袋简化成一个脑袋吃K个的最小代价就好了。想一想其实可以分两种情况:
1、M=2,就是大头吃掉的树枝+小头吃掉的树枝。
2、M&2,此时只需考虑大头吃掉的树枝,因为其他的可以根据奇偶分给不同的头吃,从而使他们不被算入最终答案。
然后就是一个简单的树形DP题,先把树转成二叉树,
然后:f[i][j][k]=min{f[lc[i]][X][0]+f[rc[i]][j-X][k]+(m==2)*(k==0)*d[i]||
&&&&&&&&&&&&&&&&&&&& f[lc[i]][X-1][1]+f[rc[i]][j-X][k]+(k==1)*d[i]}
f[i][j][k]指以i为根的子树分j个给大头吃,父亲是k(k=1被大头吃k=0被小头吃)。
dp炮兵阵地(poj1185)
一、问题描述:司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。
图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
二、分析:1. 为何状态压缩:
&&& 棋盘规模为n*m,且m≤10,如果用一个int表示一行上棋子的状态,足以表示m≤10所要求的范围。故想到用ints[num]。至于开多大的数组,可以自己用DFS搜索试试看;也可以遍历0~2^m-1,对每个数值的二进制表示进行检查;也可以用数学方法
2. 如何构造状态:
&&& 当然,在此之前首先要想到用DP(?)。之后,才考虑去构造状态函数f(...)。
&&& 这里有一个链式的限制 :某行上的某个棋子的攻击范围是2。即,第r行的状态s[i],决定第r-1行只能取部分状态s[p];同时,第r行的状态s[i],第r-1行状态s[p],共同决定第r-2行只能取更少的状态s[q]。当然,最后对上面得到的候选s[i], s[p], s[q],还要用地形的限制去筛选一下即可。
&&& 简言之,第r行的威震第r-2行,因此在递推公式(左边=右边)中,必然同时出现r,和r-2两个行标;由于递推公式中行标是连续出现的,故在递推公式中必然同时出现r, r-1和r-2三个行标。由于在递推公式中左边包含一个f(...),右边包含另一个f(...),根据抽屉原理,r, r-1, r-2中至少有两个在同一个f(...)中,因此状态函数中必然至少包括相邻两行的行号作为两个维度。这就是为什么状态函数要涉及到两(相邻的)行,而不是一行。能想到的最简单形式如下:
dp[r][i][p]:第r行状态为s[i],第r-1行状态为s[p],此时从第0行~第r行棋子的最大数目为dp[r][i][p]
滚动数组版本:
//明白该题核心算法之后,可以进一步优化,使用滚动数组。其依据为炮兵攻击范围上下2行,所以
//任意行只与其相邻的两行相互影响,所以创建一个f[2][61][61]的滚动数据即可求解。
//用滚动数组依次求出每行的最优解
递推天地-----情书抄写员矩阵连乘(斐波那契)
f[i]:=f[i-1]+k*f[i-2]
递推天地-----错位排列的递推式
f[i]:=(i-1)(f[i-2]+f[i-1]);
f[n]:=n*f[n-1]+(-1)^(n-2);
递推天地-----凸多边形分三角形方法数(卡特兰数的应用)
f[n]:=C(2*n-2,n-1)
f[k]:=C(2*k-4,k-2)div (k-1); //(k&=3)
递推天地-----Catalan数列一般形式
f[n]:=C(2k,k)div (k+1);
卡特兰数的应用
1、 括号化问题
P=a1*a2*a3*…*an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方法。
解:设P(n)表示n个元素括号化的数目。如果a1前的左括号和ai后的右括号相匹配,那么a1…ai括号化的数目为P(i),a(i+1)…an括号化的数目为P(n-i).此时括号化的数目为P(i)*P(n-i)。
a1…an括号化数目:P(n)=P(1)*P(n-1)+P(2)*P(n-2)+…+P(i)*P(n-i)+…+ P(n-1)*P(1)。
下面来证明P(n)=h(n-1)
1)P(1)=h(0)=1,P(2)=h(1)=1
2)设P(m)=h(m-1),对于m=1,2,…,n-1成立
P(n)=∑P(k)*P(n-k) (其中,k=1,2,。。。。,n-1)=∑h(k-1)*h(n-k-1)
=∑h(t)*h(n-t-2)(其中,t=0,1,。。。,n-2)
所以,n个元素括号化的数目是h(n-1)。n个矩阵链的一个括号化,与具有n-1个内节点n个叶节点的分析树相对应。
2、 有n个节点的二叉树共有多少种情形?
解:有h(n)种情形
自己的理解:
一共有a0,a1,a2,…,an共n个元素,由它们来构造二叉树。h(n)表示这n个元素一共可以构成h(n)个不同的二叉树。如果选取a0作为根节点,那么其左子树包含0个元素,左子树的数目是h(0);其右子树包含n-1个元素,右子树的数目是h(n-1);以a0为根节点的二叉树的数目是h(0)*h(n-1)。如果选取a1作为根节点,那么其左子树包含1个元素a0,左子树的数目是h(1);其右子树包含h(n-2)个元素,右子树的数目是h(n-2);以a1为根节点的二叉树的数目是h(1)*h(n-2)。如果选取ai作为根节点,其左子树包含i个元素,左子树的数目是h(i);右子树包含n-i-1个元素,右子树数目为h(n-i-1);以ai为根节的二叉树的数目是h(i)*h(n-1-i)。
总的二叉树的数目为h(n)=h(0)*h(n-1)+h(1)*h(n-2)+…+h(i)*h(n-1-i)+…+h(n-1)*h(0)
如果一共有0个节点,那么二叉树的数目为h(0)=1;如果一共有1个节点,那么二叉树的数目为h(1)=1
3、 出栈次序问题
一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?
对于每个数,必须入栈一次,出栈一次。把入栈设为状态1,出栈设为状态0。n个数的所有状态对应于n个1和n个0组成的2n位二进制数。由于等待入栈的操作数按照1。。。n的顺序入栈,入栈的次数一定大于等于出栈的次数。因此,合法的输出序列是满足下面条件的序列:由左向右扫描由n个1和n个0组成的2n位二进制数,1的累计数不小于0的累计数。
解法1:第0个符号一定是1,否则该序列不合法。假设第0个1和第k个0相匹配,那么从第1个符号到第k-1个符号,从第k+1个符号到第n-1个符号也都是一个合法的序列。可以知道,k一定是一个奇数,设k=2*i+1(i=0,1,2,…,n-1)
假设2n个符号中合法的序列数为f(2n),则f(2n)= ∑f(2i)*f(2n-2*i-2),其中,i=0,1,…,n-1。
下面证明f(2n)=h(n)
1)f(0)=h(0)=1,f(2)=h(1)=1
2)假设当对于小于等于n的任意整数m满足:f(2m)=h(m)
f(2n)= ∑f(2i)*f(2n-2*i-2),其中,i=0,1,…,n-1
&&&& =∑h(i)*h(n-i-1),其中,i=0,1,…,n-1
&&&& =h(n)
所以,1,2,…,n,共有h(n)种不同的出栈序列.由n个0和n个1组成的满足下面条件的序列总数为h(n):1的个数不小于0的个数。
解法2:证明每个不合法序列与n+1个0和n-1个1组成的序列是一一对应的
不合法的序列从左向右扫描时,必然存在某一位上首先出现m+1个0和m个1(如果第0位是0,那么第0位就是满足条件的为。如果第0位是1,那么第0位上1的个数比0的个数多1,同时因为序列不合法,必然存在某一位上0的个数比1的个数多;所以,一定存在某一位上出现1的个数比0的个数少1)。此后的2n-2m-1位上有n-m个1和n-m-1个0。如果把后边的2n-2m-1位上的0和1互换,使之成为含有n-m个0和n-m-1个1的序列,结果得由n+1个0和n-1个1组成的序列。
反过来,任何一个由n+1个0和n-1个1组成的序列。因为0的个数比1的个数多2,所以必在某一位上出现0的个数比1的个数多1。同样在后面部分0和1互换,使之成为由n个0和n个1组成的序列,即n+1个0和n-1个1组成的序列对应一个不合法的序列。
因此,不合法的2n个数的序列与由n+1个0和n-1个1组成的序列一一对应。
显然,不合法的方案数位C(2n,n-1),由此得出合法的序列数为C(2n,n)
-C(2n,n+1)=C(2n,n)/(n+1)。
4、买票找零问题
球票为50元,有2n个人排除买票,其中n个人手持50元的钞票,n个人持100元的钞票,假设售票处无零钱,问这2n个人有多少种排列方式,不至于使售票处出现找不开钱的局面。
解:h(n)。
5、凸多边形的三角剖分问题
求将一个凸多边形区域分成三角形区域的方法数。
对于有n条边(n+1个顶点)的多边形的一个三角剖分与具有n-1个叶节点的分析树对应。所以,由n+1个顶点n条边构成多边形的三角剖分数目为h(n-2).
公式:f(n)=f(2)f(n-2+1)+f(3)f(n-3+1)+……+f(n-1)f(2)。
f(2) = 1 , f(3) = 1,f(4)=2,f(5)=5,f(6)=14 …………
6、上班路径问题
一位律师在住所以北n个街区和以东n个街区工作。每天她走2n个街区去上班。如果她不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?
7、圆上的点连线问题
在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?
(1) n条直线最多分平面问题
题目大致如:n条直线,最多可以把平面分为多少个区域。
f(n)=f(n-1)+n==&n(n+1)/2+1
(2) 折线分平面(hdu2050)
根据直线分平面可知,由交点决定了射线和线段的条数,进而决定了新增的区域数。当n-1条折线时,区域数为f(n-1)。为了使增加的区域最多,则折线的两边的线段要和n-1条折线的边,即2*(n-1)条线段相交。那么新增的线段数为4*(n-1),射线数为2。但要注意的是,折线本身相邻的两线段只能增加一个区域。
f(n)=f(n-1)+4(n-1)+2-1==&2n^2-n+1
(3) 封闭曲线分平面问题
题目大致如设有n条封闭曲线画在平面上,而任何两条封闭曲线恰好相交于两点,且任何三条封闭曲线不相交于同一点,问这些封闭曲线把平面分割成的区域个数。
析:当n-1个圆时,区域数为f(n-1).那么第n个圆就必须与前n-1个圆相交,则第n个圆被分为2(n-1)段线段,增加了2(n-1)个区域。
f(n)=f(n-1)+2(n-1)==&n^2-n+2
(4)平面分割空间问题(hdu1290)
由二维的分割问题可知,平面分割与线之间的交点有关,即交点决定射线和线段的条数,从而决定新增的区域数。试想在三维中则是否与平面的交线有关呢?当有n-1个平面时,分割的空间数为f(n-1)。要有最多的空间数,则第n个平面需与前n-1个平面相交,且不能有共同的交线。即最多有n-1 条交线。而这n-1条交线把第n个平面最多分割成g(n-1)个区域。(g(n)为(1)中的直线分平面的个数)此平面将原有的空间一分为二,则最多增加g(n-1)个空间。
故:f(n)=f(n-1)+g(n-1)==&(n^3+5n)/6+1&&& ps:g(n)=n(n+1)/2+1
一个环形的花坛(不包括中心,中心可能建喷泉 - -!)分成n块,然后有m种颜色的花可供选取,要求相邻区域颜色不能相同,共有多少种方法
天使告诉小杉,每只翅膀都有长度,两只翅膀的长度之比越接近黄金分割比例,就越完美。现在天使给了小杉N只翅膀,小杉想挑出一对最完美的。
输入格式:每组测试数据的
第一行有一个数N(2&=N&=30000)
第二行有N个不超过1e5的正整数,表示N只翅膀的长度。
20%的数据N&=100
输出格式:对每组测试数据输出两个整数,表示小杉挑选出来的一对翅膀。
注意,比较短的在前,如果有多对翅膀的完美程度一样,请输出最小的一对。
一、问题描述
公元11380年,一颗巨大的陨石坠落在南极。于是,灾难降临了,地球上出现了一系列反常的现象。当人们焦急万分的时候,一支中国科学家组成的南极考察队赶到了出事地点。经过一番侦察,科学家们发现陨石上刻有若干行密文,每一行都包含5个整数:
0 0 6 3 57
8 0 11 3 2845
著名的科学家SS发现,这些密文实际上是一种复杂运算的结果。为了便于大家理解这种运算,他定义了一种SS表达式:
1.&&&&&&&&&&&&&SS表达式是仅由‘{’,‘}’,‘[’,‘]’,‘(’,‘)’组成的字符串。
2.&&&&&&&&&&&&&一个空串是SS表达式。
3.&&&&&&&&&&&&&如果A是SS表达式,且A中不含字符‘{’,‘}’,‘[’,‘]’,则(A)是SS表达式。
4.&&&&&&&&&&&&&如果A是SS表达式,且A中不含字符‘{’,‘}’,则[A]是SS表达式。
5.&&&&&&&&&&&&&如果A是SS表达式,则{A}是SS表达式。
6.&&&&&&&&&&&&&如果A和B都是SS表达式,则AB也是SS表达式。
{{[[(())]]}}
都是SS表达式。
不是SS表达式。
一个SS表达式E的深度D(E)定义如下:
例如(){()}[]的深度为2。
密文中的复杂运算是这样进行的:
设密文中每行前4个数依次为L1,L2,L3,D,求出所有深度为D,含有L1对{},L2对[],L3对()的SS串的个数,并用这个数对当前的年份11380求余数,这个余数就是密文中每行的第5个数,我们称之为“神秘数”。
密文中某些行的第五个数已经模糊不清,而这些数字正是揭开陨石秘密的钥匙。现在科学家们聘请你来计算这个神秘数。
输入文件(secret.in)
共一行,4个整数L1,L2,L3,D。相邻两个数之间用一个空格分隔。
(0≤L1≤10,0≤L2≤10,0≤L3≤10,0≤D≤30)
输出文件(secret.out)
共一行,包含一个整数,即神秘数。
二、分析解答
这是一个典型的计数问题。
动态规划的一个重要应用就是组合计数—如鱼得水,具有编程简单、时空复杂度低等优点。我们自然想到:是否本题也可以用动态规划来解决呢?
条件的简化
题目对于什么是SS表达式做了大量的定义,一系列的条件让我们如坠雾中。为了看清SS表达式的本质,有必要对条件进行简化。
条件1描述了SS表达式的元素。
条件3、4、5实际上对于()、[]、{}的嵌套顺序做了限制,即()内不能嵌套[]、{},[]内不能潜逃{}。概括起来是两点:SS表达式中括号要配对;{}、[]、()从外到内依次嵌套。
状态的表示
这是动态规划过程中首先要解决的一个问题。本题的条件看似错综复杂,状态不易提炼出来,实际上,题目本身已经为我们提供了一个很好的状态表示法。
对于一个表达式来说,它含有的元素是“(”,“)”,“[”,“]”,“{”,“}”,此外,定义了深度这一概念。最简单的一种想法是:按照题目的所求,直接把{}的对数l1、[]的对数l2、()的对数l3以及深度d作为状态表示的组成部分,即用(l1,l2,l3,d)这样一个四元组来确定一个状态。令F(l1,l2,l3,d)表示这样一个状态所对应的神秘数,于是F(L1,L2,L3,D)对应问题答案。此外,我们令G(l1,l2,l3,d)表示含有l1个{},l2个[],l3个(),深度不大于d的表达式个数。显然,F(l1,l2,l3,d)=G(l1,l2,l3,d)-G(l1,l2,l3,d-1)。于是求解F的问题,可以转化为求解G的问题。
状态转移方程的建立
设当前的状态为(l1,l2,l3,d),根据表达式的第一位的值,分如下三种情况:
情况一:第一位是“(”,与其配对的“)”位于第i位。设表示这种情况下的总数,、类似定义。
()将整个表达式分成两部分(图中的ss1和ss2)。根据乘法原理,我们只需对两部分分别计数,然后乘起来即为结果。
我们设ss1中含有x对{},y对[],z对()。因为ss1外层已经由一对()括起来,故其内部不可再含[]、{},因此x=0,y=0,且ss1的深度不可超过d-1,ss1的数目为G(x,y,z,d-1)=G(0,0,z,d-1)。ss2中含有l1-x=l1个{},l2-y=l2个[],l3-z-1个(),深度不可超过d,ss2的数目为G(l1,l2,l3-z-1,d)。据此,我们写出下面这个式子:
情况一计算的复杂度为O(n^5)。
情况二:第一位是“[”,与其配对的“]”位于第i位。
============================
dp[l1][l2][l3][dd]表示深度小于等于dd的方案数,那么答案是dp[l1][l2][l3][dd]-dp[l1][l2][l3][dd-1]
可以用分块和整块的思想来看这个问题
好像[[()][]] 这类的我们可以说它是整块,[][](),这类的我们可以说它是分块,我们在构建的dp数组的时候就是把其当做是分块来处理,然后使用乘法原理
例如dp[l1][l2][l3][dd],我们可以先构建一个小分块,用小分块的方案数*其实括号能组成的方案数,而这个小分块,其实本身是一个整块
我们来看这个小分块,最外层可以是(),[],{}
如果是(),那么里面必定全部是(),由题意的优先级可知,所以假设用i个()组成小分块,则dp[l1][l2][l3][dd]=dp[0][0][i-1][dd-1]*dp[l1][l2][l3-i][dd]
也就是用i个()组成小分块,已经用掉一个做外层,所以只剩下i-1个,并且深度也应该下降为dd-1,而剩下l1个{},l2个[],l3-i个(),他们再组合,两者相乘
同样的,如果小分块使用[]作为外层,那么里面可以使用[]和(),整个小分块假设使用了j个[],i个(),则dp[l1][l2][l3][dd]=dp[0][j-1][i][dd-1]*dp[l1][l2-j][l3-i][dd]
意思就是,用j个[],i个()去构建小分块,已经用掉一个[]作为外层,因此深度也应该下降为dd-1,剩下l1个{},l2-j个[],l3-i个(),他们再组合,两者相乘
同样的,如果小分块使用{}作为外层,那么里面可以使用{},[],(),假设分别用了k,j,i个,那么dp[l1][l2][l3][dd]=dp[k-1][j][i][dd-1]*dp[l1-k][l2-j][l3-i][dd]
小分块用掉一个{}作为外层,深度也应该下降为dd-1,再用剩下的括号去组合,两者相乘
整个算法中需要按照优先级去枚举小分块最外层的类型,应该()在最外层,接着是[],{},这个也是 值得思考的地方
接着就是记忆化实现,感觉这东西递推不好写
搜索的话就要知道边界
1.l1=0 && l2=0 && l3=0& , dp值为1
1.dd=0时,dp值为0
一、问题描述:N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,则他们的身高满足T1&...&Ti&Ti+1&…&TK(1&=i&=K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入格式:第一行是一个整数N(2&=N&=100),表示同学的总数。第一行有n个整数,用空格分隔,第i个整数Ti(130&=Ti&=230)是第i位同学的身高(厘米)。输出格式 OutputFormat
输出文件:这一行只包含一个整数,就是最少需要几位同学出列
二、分析:分别把每个点看成中间最高的人,两边的人数就是求一下从左端到该点的最长上升子序列、从右端到该点的最长下降子序列加起来。然后判断那个点做中间点时需要去除的人数最少就行了。
(noip2006)
一、问题描述金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
电脑 打印机,扫描仪
书桌 台灯,文具
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有0个、1个或2个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是10元的整数倍)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中*为乘号)请你帮助金明设计一个满足要求的购物单。
输入格式:输入文件的第1行,为两个正整数,用一个空格隔开:
N m 其中N(&32000)表示总钱数,m(&60)为希望购买物品的个数。)
从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有3个非负整数
v p q(其中v表示该物品的价格(v&10000),p表示该物品的重要度(1~5),q表示该物品是主件还是附件。如果q=0,表示该物品为主件,如果q&0,表示该物品为附件,q是所属主件的编号)
输出格式:输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(&200000)。
二、分析:对于每个部件,如果它是主件,那么有5种决策:只要主件,要主件和附件1,要主件和附件2,全要,连主件都不要。剩下的事情就好办了。
草率的审题,可能会得到这样的算法:dp,对每一个物品做两种决策,取与不取。如果取,满足两个条件:1.要么它是主件,要么它所属的主件已经在包里了。2.放进去后的重要度与价格的成绩的总和要比没放进时的大。这两个条件缺一不可的。于是呼,得到如下的动规方程:
f[i,j]:=f[i-1,j];
& if& (i为主件or& i的附件在包中)and (f[i,j]&f[i,j-v]+v*w)
&&& then f[i,j]:=f[i,j-v]+v*w;
我们来分析一下复杂度,空间:dp的阶段为n^2,对与每一个阶段都要记录该状态下在包中的物品有哪些(因为要确定附件的主件是否在包中),每个阶段的记录都要O(n)的空间,所以总的就是O(n^3)。时间,一个dp,n^2的外层循环,内部用布尔量加个主附件的对应数组,为O(1),和起来就为O(n^2)的复杂度。可以看的出,时间的需求为32000*60,不成问题。空间,大约要7.5M的空间,在64M的要求下是完全可以的过的。如果用上题目中的一个很隐秘的条件:“每件物品都是10元的整数倍”,就可以把速度在提高十倍。
细细的看题目,还一个很重要的条件我们还没用:“每个主件可以有0个,1个或2个附件”。这貌似不起眼的一句话,却给我们降低复杂度提供了条件。想一想,为什么题目要对附件的个数做限制呢,明显是在降低难度。
对于一套物品(包含主件,所以的附件),我们称为一个属类,对一个属类的物品的购买方法,有以下5种:
&&& 1.一个都不买
&&& 2.主件
&&& 3.主件+附件1
&&& 4.主件+附件2
&&& 5.主件+附件1+附件2
这五种购买方法也是唯一的五种方法,也就是说对一属类的物品,我们只有上述的5种购买方法。
于是我们很自然的就会想到把物品按物品的属类捆在一起考虑。这样我们把物品的属类作为dp的状态。可以得到如下的dp方程:
f[i,j]=max{f[i-1,j];
&&&&&&&&&& f[i-1,j-v[i,0]]+v[i,0]*w[i,0];
&&&&&&&&&&f[i-1,j-v[i,0]-v[i,1]]+v[i,0]*w[i,0]+v[i,1]*w[i,1];
&&&&&&&&&&f[i-1,j-v[i,0]-v[i,2]]+v[i,0]*w[i,0]+v[i,2]*w[i,2];
&&&&&&&&&&f[i-1,j-v[i,0]-v[i,1]-v[i,2]]+v[i,0]*w[i,0]+v[i,1]*w[i,1]+v[i,2]*w[i,2];}
很显然时间复杂度为O(n^2),空间复杂度为O(n^2),加上利用“每件物品都是10元的整数倍”除以10的优化,本题就很完美的解决了。
初始化也十分关键。
------------------------------------------------------------------
实际上 启发式搜索+分支定界也能0.01s AC
方法就是按照重要度排序,然后检查主件附件即可
这是一个典型的树形依赖背包问题,对于这类问题有更强的O ( n *V ) 解法。
用f[x, C]表示给以x为根的子树分配v体积所能得到的最大价值。那么计算过程如下:
枚举x的每个儿子,设当前枚举的儿子为i,物品i的体积为v[i],价值为w[i];
置f[i] = f[x],之后递归求解f[i, C - v[i]];
最后更新f[x]数组,即f[x, j] = max ( f[x, j], f[i, j - v[i]] + w[i] ) j = v[i]~C
枚举x的儿子如果朴素地枚举所有n个节点话会造成时间上的浪费;由于只有两个附件,考虑采用邻接表的方式储存子结点,使得总时间复杂度提速到完美的O ( n *V ) 。还有就是本题的10的倍数的优化可以进一步提速,上面已经写有了我就不再废话。我用这个算法的提交结果是0ms。
一、&&&&&&&&&&&&&问题描述
假设你想以最美观的方式布置花店的橱窗。你有F束花,每束花的品种都不一样,同时,你至少有同样数量的花瓶,被按顺序摆成一行。花瓶的位置是固定的,并从左至右,从1至V顺序编号,V是花瓶的数目,编号为1的花瓶在最左边,编号为V的花瓶在最右边。花束则可以移动,并且每束花用1至F的整数唯一标识。标识花束的整数决定了花束在花瓶中排列的顺序,即如果I<j,则花束I必须放在花束j左边的花瓶中。
例如,假设杜鹃花的标识数为1,秋海棠的标识数为2,康乃馨的标识数为3,所有的花束在放入花瓶时必须保持其标识数的顺序,即:杜鹃花必须放在秋海棠左边的花瓶中,秋海棠必须入在康乃馨左边的花瓶中,如果花瓶的数目大于花束的数目,则多余的花瓶必须空置,每个花瓶中只能放一束花。
每一个花瓶的形状和颜色也不相同。因此,当各个花瓶中放入不同的花束时,会产生不同的美学效果,并以美学值(一个整数)来表示,空置花瓶的美学值为零。在上述例子中,花瓶与花束的不同搭配所具有的美学值,可以用下面式样的表格来表示。
花&&&&&&&&&&&&&&&&&&& 瓶
例如,根据上表,杜鹃花放在花瓶2中,会显得非常好看;但若放在花瓶4中则显得很难看。
为取得最佳美学效果,你必须在保持花束顺序的前提下,使花束的摆放取得最大的美学值。如果具有最大美学值的摆放方式不止一种,则其中任何一种摆放方式都可以接受,但你只右输出其中一种摆放方式。
假设条件(Asumption)
1≤F≤100,其中F为花束的数量,花束编号从1至F。
F≤V≤100,其中V是花瓶的数量。
-50≤Aij≤50,其中Aij 是花束i在花瓶j中时的美学值。
输入(Input)
输入文件是正文文件(text& file),文件名是flower.inp。
第一行包含两个数:F、V。
随后的F行中,每行包含V个整数,Aij即为输入文件中第(i+1)行中的第j个数。
(4)输出(Input)
输出文件必须是名为flower.out的正文文件,文件应包含两行:
第一行是程序所产生摆放方式的美学值。
第二行必须用F个数表示摆放方式,即该行的第K个数表示花束K所在的花瓶的编号。
flower.inp:
7& 23& —5& —24&16
5& 21& —4& 10& 23
—21&5& —4& —20&20
flower.out:
程序必须在2秒钟内运动完毕。
在每个测试点中,完全正确者才能得分。
《花店橱窗布置问题》讲的是:在给定花束顺序的前提下,如何将花束插入到花瓶中,才能产生最大美学值。
下面我们分析一下该题的数学模型。
三、数学模型的建立
考虑通过有向图G来建}

我要回帖

更多关于 道教徒一般分为哪两种 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信