我试图做这个问题:
给出三个整数:GA,GB和GC(代表苹果,橘子, 和香蕉分别)& N行各包含三个整数: A,B和C,代表苹果,橘子和苹果的数量 香蕉分别在那种食物中。
检查是否可以仅使用某些纸箱,以确定总数 苹果,橙子和香蕉分别总结为GA,Gb和GC。对于 每个可用的纸箱,我们只能选择购买或不购买。 他不能多次购买某个纸箱,而他也买不到 纸箱的分数。
示例测试用例
IN
100 100 100 3 10 10 40 10 30 10 10 60 50
OUT
没有
IN
100 100 100 5 40 70 30 30 10 40 20 20 50 10 50 90 40 10 20
OUT
是
对于这个问题,我已经编写了一些代码,但只是出现了分段错误和一些错误。另外,我的算法非常糟糕。我所做的是找到苹果数组的所有子集,使得它们的总和是GA,然后我检查这些集合中是否有橙子和香蕉添加到GB和GC。但是这个想法非常缓慢而且很难编码......
我认为这有点是背包问题的变化,可以以更好的复杂性来解决(至少比O(2 ^ N)[我当前的复杂性]更好; P)。那么,什么是更好的算法来解决这个问题,而且,请参阅我在PasteBin的当前代码(我没有将代码放在stackoverflow上因为它有缺陷,而且,我相信我会有用它开始从头开始...)
答案 0 :(得分:1)
分段错误完全是您的问题。
背包是NP完全的,所以这是(假设输入A,B,C总是相同的,并且Ga = A的总和的一半)。我认为没有人要求你在这里解决NP完全问题。
显然,您不检查所有集合,而只检查总和A <= 100,总和B <= 100,总和C <= 100的集合。
答案 1 :(得分:1)
与此question相同的情况。
此问题是来自Facebook Hackercup资格回合的第二个问题,当前正在进行中(它将于1月12日12月1日UTC结束)。
在这里问一些关于积极编程竞赛问题的解决方案并不公平。
答案 2 :(得分:0)
这是 0-1背包问题的变体。这个问题是NP难的,所以没有太多的希望在多项式时间内找到解,但在<em>伪多项式时间内存在一个解决方案,这使得这个问题相当容易(在复杂的世界中)问题)。
该算法的工作原理如下:
<0,0,0>
。<a',b',c'>
:迭代集合中的所有元组<a,b,c>
并将<a+a',b+b',c+c'>
添加到集合中,确保未添加重复项。不要添加一个或多个元素超过相应目标值的元组。 可选但强烈建议 下限消除:您还可以执行前瞻,例如消除所有永远不会到达给定目标的值(比如说)你最多可以添加20
个苹果,然后可以减少所有小于80
个苹果的值。
概念1(下限):由于您将元组的值添加到一起,现在如果有元组
<a0,a1,a2>
和<b0,b1,b2>
,则添加这些元组最多会增加一个<a0+b0,a1+b1,a2+b2>
的元组。现在说目标是<t0,t1,t2>
然后您可以安全地消除元组<q0,q1,q2>
如果q0+a0+b0 < t0
(概括为其他元组元素),因为即使您可以添加最后的元组,它也永远不会到达所需的值。因此下限是<t0-a0-b0,t1-a1-b1,t2-a2-b2>
。您可以将此概括为 n 元组。
首先,将所有提供的元组加在一起(对于第二个实例,即<140,160,230>
),然后从目标中减去该元组(结果是:<-40,-60,-130>
)。每次迭代时,下限随着该纸箱而增加,因此在第一次迭代后,第二个示例的结果为(<-40+40,-60+70,-130+30>
或<0,10,-100>
)。
然而,时间复杂度 O(ta ^ 3 tb ^ 3 tc ^ 3) ta , tb 和 tc 目标值。
示例1 (两个给定测试用例的高级别):
INPUT
100 100 100
3
10 10 40
10 30 10
10 60 50
集合以{<0,0,0>}
开头,每次迭代后我们得到:
{<0,0,0>}
; {<0,0,0>,<10,10,40>}
; {<0,0,0>,<10,10,40>,<10,30,10>,<20,40,50>}
;和{<0,0,0>,<10,10,40>,<10,30,10>,<20,40,50>,<10,60,50>,<10,60,50>,<20,70,90>,<30,100,100>}
因此失败。使用 underbound-elimination :
{<0,0,0>}
,下限<100-30,100-100,100-100>=<70,0,0>
因此消除了<0,0,0>
。{}
因此打印“no”。示例2
INPUT
100 100 100
5
40 70 30
30 10 40
20 20 50
10 50 90
40 10 20
使用下限消除:
{<0,0,0>}
下限:<-40,-60,-130>
因此确定。{<0,0,0>,<40,70,30>}
下限:<0,10,-100>
(消除<0,0,0>
,因为第二次冲突)。{<40,70,30>,<70,80,70>}
下限:<30,20,-60>
(无消除)。{<40,70,30>,<70,80,70>,<60,90,80>,<90,100,120>}
下限:<50,40,-10>
(消除<40,70,30>
)上消除<90,100,120>
。{<70,80,70>,<60,90,80>,<80,130,160>,<70,140,170>}
下限:<60,90,80>
(消除<70,80,70>
)上消除<80,130,160>
和<70,140,170>
。{<60,90,80>,<100,100,100>}
下限:<100,100,100>
(消除<60,90,80>
)。{<100,100,100>}
因此“是”。Haskell计划
我已经实现了一个(不是那么高效,但概念证明)Haskell程序,可以完成任意元组长度的技巧:
import qualified Data.Set as Set
tupleSize :: Int
tupleSize = 3
group :: Int -> [a] -> [[a]]
group _ [] = []
group n l = take n l : group n (drop n l)
empty :: Int -> Set.Set [Int]
empty n = Set.fromList [replicate n 0]
solve :: [Int] -> [[Int]] -> Bool
solve t qs = Set.member t $ mix t (lowerBound t qs) qs $ empty $ length t
lowerBound :: [Int] -> [[Int]] -> [Int]
lowerBound = foldl (zipWith (-))
lowerCheck :: [Int] -> [Int] -> Bool
lowerCheck l x = and $ zipWith (<=) l x
targetCheck :: [Int] -> [Int] -> Bool
targetCheck t x = and $ zipWith (>=) t x
takeout :: Int -> [a] -> [a]
takeout _ [] = []
takeout i (h:hs) | i == 0 = hs
| otherwise = h : takeout (i-1) hs
mix :: [Int] -> [Int] -> [[Int]] -> Set.Set [Int] -> Set.Set [Int]
mix _ _ [] s = s
mix t l (q:qs) s = mix t (zipWith(+) l q) qs $ Set.filter (lowerCheck l) $ Set.union s $ Set.filter (targetCheck t) $ Set.map (zipWith (+) q) s
reply :: Bool -> String
reply True = "yes"
reply False = "no"
main = interact $ \x -> let tuples = group tupleSize $ takeout tupleSize $ map read (words x) in reply $ solve (head tuples) (tail tuples)
您可以使用以下命令编译它:
ghc file.hs
./file < input
结论:虽然最坏情况的行为可能很难,但第二个例子表明在某些情况下可以有效解决问题。