多校8-1003-hdu4372-Count the Building题解

来源:互联网 发布:java输出数组全部元素 编辑:程序博客网 时间:2024/05/16 18:35

Brief Description:

有N个高楼,楼的高度为N的一个排列,现在从左到右能看到x个,从右往左能看到y个,问高楼一共有多少种排列。能看到高楼的前提是后面的高楼没有被前面的高楼挡住(即后面高楼的高度大于前面高楼的高度)

Analysis:

方法一: 

摘自题解+修正题解笔误+个人理解:

设dp[i][j][k] 为共i个楼,从左往后看到共j个,从右往左看共k个的方案种数 
dp2[i][j] 为共i个楼,从左往右看共j个的方案种数 
则dp2[i][j] = sum(dp[i][j][k]) 1<=k<=i 
枚举当前最高建筑物的位置,即将排列分为前后两半,分解为两个子问题 
dp[i][j][k] = sum( C(i-1,t)*dp2[t][j]*dp2[i-1-t][k] ) (0<=t<=i-1) 
其中C(n,m)为组合数 
复杂度O(n^4)。要超时。 

评注:dp[i][j][k]的状态设计还是很好想的,这个问题也是很好分解为子问题的,通过枚举最高建筑物来分解子问题,这时或许很自然的就引入了dp2[i][j].


方法二: 
dp[n][x][y]表示一共n幢楼,从左数看到x幢,从右数看到y幢,则 
dp[n][x][y] = dp[n-1][x-1][y]+dp[n-1][x][y-1]+(n-2)*(dp[n-1][x][y])
就是枚举一下最低的楼的位置,最左,最右,和中间。 
复杂度 O(n^3) 。要超时。

评注:方法一用的是最高建筑物来分解子问题,方法二用的是最低,似乎更简单了?


方法三:

f[n][k] 表示单边看过去,共 n 幢楼,最高楼在另一端,看到了 k 幢的排列个数。于是 
f[n][k] = sigma(f[x][k - 1] * C[n - 2][x - 1] * (n - x - 1)!) 
= sigma(f[x][k - 1] * (n - 2)! / (x - 1)!) 
= f[n - 1][k] * (n - 2) + f[n - 1][k - 1] 
于是算答案就是 answer(n, x, y) = sigma(f[i][x] * f[n - i + 1][y] * C[n - 1][i - 1]) 。 (也是枚举最高建筑物)
预处理 O(n^2),每组 case O(n)。不过还是要超时。 

评注:这种DP设计状态的方式也很常见。f[n][k]转为f[n-1][k],f[n-1][k-1]的式子变化需要一点点的小技巧。


方法四: 
n^3的算法暴力出后找规律可得dp[n][i][j] = dp[n][i - 1][j + 1] * j / (i - 1)。 
其中dp[n][1][i] = S1[n - 1][i - 1] 
进一步化简得到dp[n][i][j] = S1[n - 1][i + j - 2] * C[i + j - 2][i - 1] 
评注:暂时不是很懂,还是得看一下stirling数和catalan数


总结:这道题让我感受到了组合计数问题的奇妙,让我很想学一下几种常用的组合计数和各种计数的思维方式。+U!


原创粉丝点击