您的位置:首页 > 职场人生

2009年9月刊《程序员》算法题之我见——思索之三

2009-10-08 20:53 330 查看
本系列文章目录

2009年9月刊《程序员》算法题之我见——思索之一
2009年9月刊《程序员》算法题之我见——思索之二
2009年9月刊《程序员》算法题之我见——思索之三

接上文“2009年9月刊《程序员》算法题之我见——思索之二

仔细分析后发现,似乎不能推导出一个统一的公式来计算出大于三行的可能数。只能另辟蹊径。

问题七:M=15,N=2,P=S,求方案总数
这个问题现在的解决方法如下
第一步:先得到每一行P=2S的方案数以及每一个方案

第二步:将每一个方案一拆二,分为S和S。这表示这两个方案使能够相邻的,将相邻的可能压入一个表,用来备用。这一步的理由参看上一篇文章

第三步:初始化第一行的每一种可能,每一种的可能都计数为1。从第二行开始,通过查表,得知上一行的每一个方案在这一行的相邻方案是什么,然后计数。随着行数的增多,每种方案在每一行出现的可能数都会发生变化。

第四步:到最后一行,统计最后一行每一种方案的各自可能数。然后求总和。

经过测试,M=15,N=150,P=4的解为70种,和之前的问题五的答案一致。当M=15,N=4,P=2的解出乎我的意料,为3328134种。不知其他人的答案是否和我的一致,欢迎交流。
下面详细分析每一步,并贴代码,用的是VB2005

函数
Public Function CacuInternSiteCount(ByVal Line As Integer, ByVal SitePerLine As Integer, ByVal InternPerLine As Integer) As Integer
Line表示行数
SitePerLine表示每一排的座位数
InternPerLine表示每一排的实习生数
返回方案总数
先是变量定义:

Dim i As Integer, j As Integer
Dim tP() As Integer, tP1() As Integer
Dim tL As New List(Of Array)

Dim S1 As Integer, S2 As Integer, S3 As Integer
Dim tMap As New clsInternMap
Dim tF As New Dictionary(Of Integer, Integer)
Dim tF2 As New Dictionary(Of Integer, Integer)


都是临时变量,后面再详细介绍。

预备步:
For i = 0 To clsCombination.C(InternPerLine, 2 * InternPerLine) - 1
tP1 = clsCombination.GetCombination(0, 2 * InternPerLine - 1, InternPerLine, i)
tL.Add(tP1)
Next


在后面的第二步计算中,要将每一个方案一拆二。而一拆二的依据就是利用组合原理,从2S中选出S的组合来,这个利用的组合函数,以及获得组合的方法在详细可以参见“遍历组合的实现——VB2005”和“遍历排列的实现——VB2005”这两篇文章。

第一步:先得到每一行P=2S的方案数以及每一个方案。这里得到的每一个方案也是调用组合函数。得到一个组合再判断这个组合是否符合题目的要求,用的是IsOk这个函数。
第二步:将每一个方案一拆二,分为S和S。这表示这两个方案使能够相邻的,将相邻的可能压入一个表,用来备用。
For i = 0 To clsCombination.C(2 * InternPerLine, SitePerLine) - 1
tP = clsCombination.GetCombination(0, SitePerLine - 1, 2 * InternPerLine, i)

If IsOk(tP) = True Then

S1 = 0
For j = 0 To tP.GetUpperBound(0)
S1 = S1 Or (1 << tP(j))
Next

For Each tP1 In tL
S2 = 0
For j = 0 To tP1.GetUpperBound(0)
S2 = S2 Or (1 << tP(tP1(j)))
Next
S3 = S1 - S2
tMap.Add(S2, S3)
Next
End If
Next


这里调用了IsOk函数,这是判断实习生的位置是否符合题目的要求,代码如下:
Public Function IsOk(ByVal tP() As Integer) As Integer
Dim tB As Boolean = False
For i = 1 To tP.GetUpperBound(0)
If tP(i) - tP(i - 1) < 2 Then
Return False
End If
Next
Return True
End Function

还有一个注意的地方,很多人以为VB2005是没有移位运算的,可其实是有的。这里的S1就是2S的一个组合,然后拆分为S2和S3的S的组合。

第三步:初始化第一行的每一种可能,每一种的可能都计数为1。从第二行开始,通过查表,得知上一行的每一个方案在这一行的相邻方案是什么,然后计数。随着行数的增多,每种方案在没一行出现的可能数都会发生变化。
For Each i In tMap.GetKeys
tF.Add(i, 1)
tF2.Add(i, 0)
Next

Dim tL2 As List(Of Integer), k As Integer, tL3 As New List(Of Integer)

tL3.AddRange(tF.Keys)

For i = 2 To Line

For Each j In tL3
tL2 = tMap.GetIndex(j)
If Not (tL2 Is Nothing) Then
For Each k In tL2
tF2(k) = tF2(k) + tF(k)
Next
End If
Next

For Each j In tL3
tF(j) = tF2(j)
tF2(j) = 0
Next j
Next


最后一步统计:统计每一种情况出现的可能数,求总和,并返回。

S1 = 0
For Each j In tL3
S1 += tF(j)
Next
Return S1


最后将上述所有代码一起贴下来。也欢迎大家交流。
Public Class clsIntern

Public Function CacuInternSiteCount(ByVal Line As Integer, ByVal SitePerLine As Integer, ByVal InternPerLine As Integer) As Integer
Dim i As Integer, j As Integer Dim tP() As Integer, tP1() As Integer Dim tL As New List(Of Array) Dim S1 As Integer, S2 As Integer, S3 As Integer Dim tMap As New clsInternMap Dim tF As New Dictionary(Of Integer, Integer) Dim tF2 As New Dictionary(Of Integer, Integer)

For i = 0 To clsCombination.C(InternPerLine, 2 * InternPerLine) - 1 tP1 = clsCombination.GetCombination(0, 2 * InternPerLine - 1, InternPerLine, i) tL.Add(tP1) Next

For i = 0 To clsCombination.C(2 * InternPerLine, SitePerLine) - 1
tP = clsCombination.GetCombination(0, SitePerLine - 1, 2 * InternPerLine, i)

If IsOk(tP) = True Then

S1 = 0
For j = 0 To tP.GetUpperBound(0)
S1 = S1 Or (1 << tP(j))
Next

For Each tP1 In tL
S2 = 0
For j = 0 To tP1.GetUpperBound(0)
S2 = S2 Or (1 << tP(tP1(j)))
Next
S3 = S1 - S2
tMap.Add(S2, S3)
Next
End If
Next

For Each i In tMap.GetKeys
tF.Add(i, 1)
tF2.Add(i, 0)
Next

Dim tL2 As List(Of Integer), k As Integer, tL3 As New List(Of Integer)

tL3.AddRange(tF.Keys)

For i = 2 To Line

For Each j In tL3
tL2 = tMap.GetIndex(j)
If Not (tL2 Is Nothing) Then
For Each k In tL2
tF2(k) = tF2(k) + tF(k)
Next
End If
Next

For Each j In tL3
tF(j) = tF2(j)
tF2(j) = 0
Next j
Next

S1 = 0
For Each j In tL3
S1 += tF(j)
Next
Return S1
End Function

Public Function IsOk(ByVal tP() As Integer) As Integer
Dim tB As Boolean = False
For i = 1 To tP.GetUpperBound(0)
If tP(i) - tP(i - 1) < 2 Then
Return False
End If
Next
Return True
End Function
End Class

Public Class clsInternMap
Private mMap As Dictionary(Of Integer, List(Of Integer))
Public Sub New()
mMap = New Dictionary(Of Integer, List(Of Integer))
End Sub

Public Sub Add(ByVal S1 As Integer, ByVal S2 As Integer)
If mMap.ContainsKey(S1) = True Then
If mMap(S1).Contains(S2) = False Then
mMap(S1).Add(S2)
End If
Else
mMap.Add(S1, New List(Of Integer))
mMap(S1).Add(S2)
End If
End Sub

Public Function GetIndex(ByVal S1 As Integer) As List(Of Integer)
If mMap.ContainsKey(S1) = True Then
Return mMap(S1)
Else
Return Nothing
End If
End Function

Public Function GetKeys() As List(Of Integer)
Dim tL As New List(Of Integer)
tL.AddRange(mMap.Keys)
Return tL
End Function
End Class
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: