Algorithm 品酒问题

Algorithm 品酒问题,algorithm,Algorithm,我几乎用了所有的比赛时间(3小时)来解决这个问题。徒劳:(也许你能帮我找到解决办法 一群Facebook员工刚刚推出了一款非常成功的产品。为了庆祝,他们决定去品酒。在葡萄园,他们决定玩一个游戏。给一个人几杯葡萄酒,每杯葡萄酒都含有不同的葡萄酒。每杯葡萄酒都贴上标签,标明其所含的葡萄酒种类。品尝完每一杯后在这些葡萄酒中,有标签的酒杯被取下,给同一个人的酒杯中有相同的葡萄酒,但没有标签。然后,这个人需要确定哪一个没有标签的酒杯中有哪一种葡萄酒。遗憾的是,小组中没有人能够区分葡萄酒,所以他们只是随机猜

我几乎用了所有的比赛时间(3小时)来解决这个问题。徒劳:(也许你能帮我找到解决办法

一群Facebook员工刚刚推出了一款非常成功的产品。为了庆祝,他们决定去品酒。在葡萄园,他们决定玩一个游戏。给一个人几杯葡萄酒,每杯葡萄酒都含有不同的葡萄酒。每杯葡萄酒都贴上标签,标明其所含的葡萄酒种类。品尝完每一杯后在这些葡萄酒中,有标签的酒杯被取下,给同一个人的酒杯中有相同的葡萄酒,但没有标签。然后,这个人需要确定哪一个没有标签的酒杯中有哪一种葡萄酒。遗憾的是,小组中没有人能够区分葡萄酒,所以他们只是随机猜测。他们总是会为每一种葡萄酒猜测不同类型的葡萄酒玻璃。如果他们做得足够正确,他们就赢了。你必须找到这个人可以赢的方式的数量,模数1051962371

输入
输入的第一行是测试用例的数量N。接下来的N行分别包含一个测试用例,由两个整数G和C组成,用一个空格分隔。G是葡萄酒的总杯数,C是一个人必须正确识别才能获胜的最小数量

约束
N=20
1.≤ G≤ 100
1.≤ CG

输出
对于每个测试用例,输出一行,其中包含一个整数,即一个人可以赢得游戏的方式数模1051962371

示例输入
5
11
4.2
5
13 10
14.1

示例输出
1
7
1
651

405146859

我的解决方案涉及使用

一个相对数D(n,k)是n个元素的排列数,其中k个元素正好位于它们的原始位置。这个问题要求至少k个元素集,所以我只取k,k+1,…,n的和

以下是我提交的Python(清理后):

从sys import stdin、stderr,将recursionlimit设置为recdepth
从数学中导入阶乘作为事实
深度(100000)
MOD=1051962371
缓存=[-1表示X范围内的i(101)]表示X范围内的j(101)]
def ncr(n,k):
返回事实(n)/事实(k)/事实(n-k)
def D(n,k):
如果缓存[n][k]=-1:
如果k==0:
如果n==0:
缓存[n][k]=1
elif n==1:
缓存[n][k]=0
其他:
缓存[n][k]=(n-1)*(D(n-1,0)+D(n-2,0))
其他:
缓存[n][k]=ncr(n,k)*D(n-k,0)
返回缓存[n][k]
返回缓存[n][k]
def应答(总计,匹配):
返回和(x范围内i的D(总计,i)(匹配,总计+1))%MOD
如果“名称”=“\uuuuuuuu主要”:
cases=int(stdin.readline())
对于xrange中的案例(案例):
写入(“案例%d:\n”%case)
G、 C=map(int,stdin.readline().split())
打印答案(G、C)

这是一个不需要事先了解合同编号的公式。(好吧,这基本上是维基上的公式证明,但我想我还是要分享一下。)

首先找到f(n):n个元素中没有固定点的排列的数量。通过包含排除公式很简单:固定k个给定点的排列的数量是(n-k)!,这些k点可以用C(n,k)的方式选择。因此,f(n)=n!-C(n,1)(n-1)!+C(n,2)(n-2)!-C(n,3)(n-3)!+

现在求出有k个不动点的置换数。这些点可以用C(n,k)的方式选择,其余的n-k点可以用f(n-k)的方式重新排列。所以,它是C(n,k)f(n-k)


最后,这个问题的答案是C(g,k)f(g-k)的和在k=c,c+1,…,g.

上,和其他人一样,我计算了我现在知道的函数,即反数,但我自己在竞赛中推导了递归方程。在不失去一般性的情况下,我们简单地假设葡萄酒的正确标签是
1,2,…,g
,也就是说,根本没有排列

让我们将函数表示为
f(g,c)
。给定g个玻璃杯,我们看第一个玻璃杯,我们可以给它贴上正确的标签,也可以给它贴上错误的标签

  • 如果我们给它贴上正确的标签,我们就可以把问题简化为从
    g-1
    眼镜中取出
    c-1
    ,即
    f(g-1,c-1)
  • 如果我们给它贴错标签,我们就有了第一个玻璃的
    g-1
    选择。对于剩余的
    g-1
    玻璃,我们必须纠正
    c
    玻璃,但是这个子问题不同于我们正在计算的
    f
    ,因为在
    g-1
    玻璃中,已经有一个不匹配的玻璃。更准确地说,对于t首先,我们的答案是
    j
    ,而不是正确的标签
    1
    。让我们假设有另一个函数
    h
    为我们计算它
所以我们有
f(g,c)=f(g-1,c-1)+(g-1)*h(g-1,c)

现在计算<代码> h(g,c)< /代码>,我们需要在<代码> j>代码>玻璃>下考虑两种情况。

  • 如果我们把它标为
    1
    ,我们就把问题归结为
    f(g-1,c)
  • 如果我们给它标上
    k
    ,我们就有了
    g-1
    选择,问题就简化为
    h(g-1,c)
所以我们有
h(g,c)=f(g-1,c)+(g-1)*h(g-1,c)

下面是Haskell中的完整程序,带有备忘录和一些调试支持

import Control.Monad
import Data.MemoTrie
--import Debug.Trace

trace = flip const

add a b = mod (a+b) 1051962371
mul a b = mod (a*b) 1051962371

main = do
  (_:input) <- liftM words getContents
  let map' f [] = []
      map' f (a:c:xs) = f (read a) (read c) : map' f xs
  mapM print $ map' ans input

ans :: Integer -> Integer -> Integer
ans g c = foldr add 0 $ map (f g) [c..g]

memoF = memo2 f
memoH = memo2 h

-- Exactly c correct in g
f :: Integer -> Integer -> Integer
f g c = trace ("f " ++ show (g,c) ++ " = " ++ show x) x
  where x = if c < 0 || g < c then 0
            else if g == c then 1
            else add (memoF (g-1) (c-1)) (mul (g-1) (memoH (g-1) c))

-- There's one mismatching position in g positions
h :: Integer -> Integer -> Integer
h g c = trace ("h " ++ show (g,c) ++ " = " ++ show x) x
  where x = if c < 0 || g < c then 0
            else add (memoF (g-1) c) (mul (g-1) (memoH (g-1) c))
import-Control.Monad
导入数据.MemoTrie
--导入调试跟踪
跟踪=翻转常数
添加一个b=mod(a+b)1051962371
mul a b=mod(a*b)1051962371
main=do
(:输入)整数->整数
ans g c=foldr添加0$map(f g)[c..g]
memoF=Memo2F
memoH=memoH
--完全正确
整数->整数->整数
f g c=跟踪(“f”++显示(g,c)+”=“++显示x)x
其中x=如果c<0 | | g