{面试}{技术}一道分组打比赛的题

10 views
Skip to first unread message

Shuo Chen

unread,
Dec 28, 2009, 8:38:19 AM12/28/09
to TopLanguage
http://www.javaeye.com/topic/88204

前面那道题太简单了,这里来一道稍微难点的。

有 N 个人,分成 M 组(2<=M<=N)打比赛,每组内部不竞争,每个人和不在同一组的其他人每人打一场。

比如有ABCD四个人,如果分2组,{ABC}{D}要打3场,{AB}{CD}打4场,
分3组{A}{B}{CD}打5场,分4组{A}{B}{C}{D}打6场。显见没有哪种分组能只打1场。

问题:有 N 个人,有没有哪种分组恰好打 K 场比赛?

bool fight(int N, int K);

2<=N<=100, 1 <= K <= 10000.

jun lin

unread,
Dec 28, 2009, 8:40:46 AM12/28/09
to pon...@googlegroups.com
能不能给点实际的题目?比如猪与鸡之类的?

2009/12/28 Shuo Chen <gian...@gmail.com>

kscinow kscinow

unread,
Dec 28, 2009, 9:08:09 AM12/28/09
to pon...@googlegroups.com
呵呵 楼上很幽默啊...

这个最容易想到的就是DP吧,枚举最后一组的人数,



2009/12/28 jun lin <linjun...@gmail.com>



--
--
With Best Regards.

Name: H.J.Xie
Institute: Computer Science & Technology Department, NanJing University, NanKing, China.
             State Key Laboratory on Novel Software Technology
Email: ksc...@gmail.com
--

sagasw

unread,
Dec 28, 2009, 10:09:31 AM12/28/09
to pon...@googlegroups.com
你的稍微难一些以及前一个都是题意不清,为何不把原题贴上来呢?

-------------------------


为了促进各部门员工的交流,百度举办了一场全公司范围内的“拳皇”(百度内部最流行的格斗游戏)友谊赛,负责组织这场比赛的是百度的超级“拳皇”迷W.Z。W.Z不想用传统的淘汰赛或者循环赛的方式,而是自己制定了一个比赛规则。

由于一些员工(比如同部门或者相邻部门员工)平时接触的机会比较多,为了促进不同部门之间的交流,W.Z希望员工自由分组。不同组之间的每两个人都会进行一场友谊赛而同一组内的人之间不会打任何比赛。

比如4个人,编号为1~4,如果分为两个组并且1,2一个组,3,4一个组,那么一共需要打四场比赛:1 vs 3,1 vs 4,2 vs 3,2 vs 4。 而如

果是1,2,3一组,4单独一组,那么一共需要打三场比赛 1 vs 4,2 vs 4,3 vs 4。


很快W.Z意识到,这样的比赛规则可能会让比赛的场数非常多。W.Z想知道如果有N个人,通过上面这种比赛规则,总比赛场数有可能为K场吗?

比如3个人,如果只分到一组则不需要比赛,如果分到两组则需要2场比赛,如果分为三组则需要3场比赛。但是无论怎么分都不可能恰需要1场比赛。


相信作为编程高手的你一定知道该怎么回答这个问题了吧? 那么现在请你帮助W.Z吧。


输入要求:
每行为一组数据,包含两个数字 N, K(0N=500, K=0)。例:
2 0
2 1
3 1
3 2
样例:in.txt



输出要求:
对输入的N,K 如果N个员工通过一定的分组方式可以使比赛场数恰好为K,则输出YES,否则输出NO(请全部使用大写字母),每组数据占一行。例:
YES
YES
NO
YES
样例:out.txt


评分规则:
1.程序将运行在一台Linux机器上(内存使用不作严格限制),在每一测试数据集上运行不能超过10秒,否则该用例不得分;
2.要求程序能按照输入样例的格式读取数据文件,按照输出样例的格式将运行结果输出到标准输出上。如果不能正确读入数据和输出数据,该题将不得分;
3.该题目共有3个测试数据集,每个测试数据集为一个输入文件。各测试数据集占该题目分数的比例分别为30%,30%,40%;
4.该题目20分。


------------------------------------
C++, Lua, living in Dalian
http://sunxiunan.com/
http://twitter.com/sagasw
------------------------------------


2009/12/28 Shuo Chen <gian...@gmail.com>

sagasw

unread,
Dec 28, 2009, 10:12:01 AM12/28/09
to pon...@googlegroups.com
BTW,tag是面试,是你们公司用这个题目做面试么?



------------------------------------
C++, Lua, living in Dalian
http://sunxiunan.com/
http://twitter.com/sagasw
------------------------------------


Shuo Chen

unread,
Dec 28, 2009, 10:54:30 AM12/28/09
to TopLanguage
不是,这不是我们公司的面试题。
要是的话,我不可能把它们贴上来。

On Dec 28, 11:12 pm, sagasw <sag...@gmail.com> wrote:
> BTW,tag是面试,是你们公司用这个题目做面试么?
>
> ------------------------------------

> C++, Lua, living in Dalianhttp://sunxiunan.com/http://twitter.com/sagasw
> ------------------------------------
>
> 2009/12/28 Shuo Chen <giantc...@gmail.com>

Shuo Chen

unread,
Dec 28, 2009, 10:59:06 AM12/28/09
to TopLanguage
我把题目要求简化为写一个函数:

bool fight(int N, int K);

这样不涉及文件读写等次要因素。再说我也没有那些测试集,评分标准自然也用不上了。

> C++, Lua, living in Dalianhttp://sunxiunan.com/http://twitter.com/sagasw
> ------------------------------------
>
> 2009/12/28 Shuo Chen <giantc...@gmail.com>

sagasw

unread,
Dec 28, 2009, 9:00:34 PM12/28/09
to pon...@googlegroups.com
今天上班了,没那么多时间思考,只有趁着build时间写写纸面上的演算。

这个问题像是一个数学归纳问题(当然应该有更简洁的解法),我简单列了一下5个人的情况,

{1} {1} {1} {1} {1} 一人一组的话,那就是 4!
{2} {1} {1} {1} 这种情形是2!+ 2 * 3
{3} {1} {1} 这种 3 * 2 + 1!
{4} {1} 这种 4 * 1
{2} {2} {1} 这种2* 2 + 2 * 1 + 2 * 1
{2} {3} 这种是 2 * 3

归纳起来就是分组成员为1的个数为iOne + 1,
分组成员大于1(小于N)的个数为iNa, iNb, iNc
那么结果应该是iOne ! + iOne * iNa + iOne * iNb + iOne * iNc + iNb * iNc
当然这只是非常粗略的想,也许是错误的。

这时候问题就可以分成两部分,一部分是把人数分组,这个需要继续想想,
另外一部分就是分组以后如何计算,如上所述,


------------------------------------
C++, Lua, living in Dalian
http://sunxiunan.com/
http://twitter.com/sagasw
------------------------------------


2009/12/28 Shuo Chen <gian...@gmail.com>

张沈鹏

unread,
Dec 29, 2009, 12:51:52 AM12/29/09
to pon...@googlegroups.com


2009/12/29 sagasw <sag...@gmail.com>

今天上班了,没那么多时间思考,只有趁着build时间写写纸面上的演算。

这个问题像是一个数学归纳问题(当然应该有更简洁的解法),我简单列了一下5个人的情况,

{1} {1} {1} {1} {1} 一人一组的话,那就是 4!
{2} {1} {1} {1} 这种情形是2!+ 2 * 3
{3} {1} {1} 这种 3 * 2 + 1!
{4} {1} 这种 4 * 1
{2} {2} {1} 这种2* 2 + 2 * 1 + 2 * 1
{2} {3} 这种是 2 * 3

归纳起来就是分组成员为1的个数为iOne + 1,
分组成员大于1(小于N)的个数为iNa, iNb, iNc
那么结果应该是iOne ! + iOne * iNa + iOne * iNb + iOne * iNc + iNb * iNc
当然这只是非常粗略的想,也许是错误的。

这时候问题就可以分成两部分,一部分是把人数分组,这个需要继续想想,

 一个从上而下的n层格子,mi 为第i层的格子数,当mi>=mi+1(i=1,2,...,n-1) ,即上层的格子数不少于下层的格子数时,称之为Ferrers图像
http://zh.wikipedia.org/wiki/%E6%95%B4%E6%95%B8%E5%88%86%E6%8B%86




 



--
卖空间 http://stdyun.com/vhost
写书 http://kanrs.com
豆瓣 http://www.douban.com/people/zuroc
博客 http://zsp.javaeye.com

张沈鹏

unread,
Dec 29, 2009, 1:01:43 AM12/29/09
to pon...@googlegroups.com
http://code.activestate.com/recipes/218332/

整数拆分

简单版本

def partitions(n):
# base case of recursion: zero is the sum of the empty list
if n == 0:
yield []
return

# modify partitions of n-1 to form partitions of n
for p in partitions(n-1):
yield [1] + p
if p and (len(p) < 2 or p[1] > p[0]):
yield [p[0] + 1] + p[1:]

快速版本

def gen_partitions_ms(n):
"""Generate all partitions of integer n (>= 0).

Each partition is represented as a multiset, i.e. a dictionary
mapping an integer to the number of copies of that integer in
the partition. For example, the partitions of 4 are {4: 1},
{3: 1, 1: 1}, {2: 2}, {2: 1, 1: 2}, and {1: 4}. In general,
sum(k * v for k, v in a_partition.iteritems()) == n, and
len(a_partition) is never larger than about sqrt(2*n).

Note that the _same_ dictionary object is returned each time.
This is for speed: generating each partition goes quickly,
taking constant time independent of n.
"""

if n &lt; 0:
raise ValueError("n must be >= 0")

if n == 0:
yield {}
return

ms = {n: 1}
keys = [n] # ms.keys(), from largest to smallest
yield ms

while keys != [1]:
# Reuse any 1's.
if keys[-1] == 1:
del keys[-1]
reuse = ms.pop(1)
else:
reuse = 0

# Let i be the smallest key larger than 1. Reuse one
# instance of i.
i = keys[-1]
newcount = ms[i] = ms[i] - 1
reuse += i
if newcount == 0:
del keys[-1], ms[i]

# Break the remainder into pieces of size i-1.
i -= 1
q, r = divmod(reuse, i)
ms[i] = q
keys.append(i)
if r:
ms[r] = 1
keys.append(r)

yield ms

Mikster.Z

unread,
Dec 29, 2009, 1:02:48 AM12/29/09
to pon...@googlegroups.com
F(n)的可能取值是F(n-x)+F(x)+x*(n-x) | x<n/2
F(1)=0
F(2) = 0,1
F(3) = 0,2,3
.....
最后实现这样好一点k-x*(n-x)拆分成两个整数(>=0),检查F(n-x),F(x)是否有符合要求的数~~

复杂度n,k有限制,所以复杂度应该还是可控的~~

2009/12/29 sagasw <sag...@gmail.com>



--
EX - EMBEDDED SYSTEM DEVELOPER
SOFTWARE ENGINEER
Name : Mikster  

Hongzhang Liu

unread,
Dec 29, 2009, 1:09:35 AM12/29/09
to pon...@googlegroups.com
做一点分析就会发现,K = (N*N - sigma(m(i) * m(i))) / 2,其中m(i)是每组人数。实际上就是看是否存在一个拆分,使得拆分得到的各数的平方和为N*N - 2K

2009/12/29 张沈鹏 <zsp...@gmail.com>

Shuo Chen

unread,
Dec 29, 2009, 1:11:23 AM12/29/09
to TopLanguage
用整数划分来做,正确性是没问题的,效率堪忧。因为这等于是穷举了。

Mikster.Z

unread,
Dec 29, 2009, 1:13:59 AM12/29/09
to pon...@googlegroups.com
没啥时间细想,随便趁building蛮想的,也许有错~

因为K有限制的,另外还可以记忆化,效率应该没问题的

2009/12/29 Shuo Chen <gian...@gmail.com>

sagasw

unread,
Dec 29, 2009, 1:15:58 AM12/29/09
to pon...@googlegroups.com
没有一个给出完整答案的,看来是有些难。


------------------------------------
C++, Lua, living in Dalian
http://sunxiunan.com/
http://twitter.com/sagasw
------------------------------------


2009/12/29 Mikster.Z <china...@gmail.com>

Mikster.Z

unread,
Dec 29, 2009, 1:19:42 AM12/29/09
to pon...@googlegroups.com
faint,大家都在上班,这个已经极限操作了~~

2009/12/29 sagasw <sag...@gmail.com>

张沈鹏

unread,
Dec 29, 2009, 1:20:47 AM12/29/09
to pon...@googlegroups.com
2009/12/29 Shuo Chen <gian...@gmail.com>
用整数划分来做,正确性是没问题的,效率堪忧。因为这等于是穷举了。

可以剪范围的

不过我先写一个最简单的版本:)

python就是好啊,我几乎一行算法代码都没写就做完了

还要写页面呢,忙啊忙。。。


def _partitions(n):

    """Generate all partitions of integer n (>= 0).

    Each partition is represented as a multiset, i.e. a dictionary
    mapping an integer to the number of copies of that integer in

    the partition.  For example, the partitions of 4 are {4: 1},
    {3: 1, 1: 1}, {2: 2}, {2: 1, 1: 2}, and {1: 4}.  In general,
    sum(k * v for k, v in a_partition.iteritems()) == n, and
    len(a_partition) is never larger than about sqrt(2*n).


    Note that the _same_ dictionary object is returned each time.
    This is for speed:  generating each partition goes quickly,
    taking constant time independent of n.
    """

    if n < 0:
def partitions(n):
    for i in _partitions(n):
        result = []
        for k, v in i.iteritems():
            result.extend([k]*v)
        yield tuple(result)

from itertools import combinations

def get_all_k(n):
    poss = set()
    for i in partitions(n):
        result = 0
        for a, b in combinations(i, 2):
            result += a*b
        poss.add(result)
    return poss

def can_m2k(m,k):
    a = get_all_k(m)
    return k in a

print can_m2k(4,3)

sagasw

unread,
Dec 29, 2009, 1:22:01 AM12/29/09
to pon...@googlegroups.com
公式都是给的刷刷的,
貌似写到实际代码就有些费劲了

张沈鹏

unread,
Dec 29, 2009, 1:24:56 AM12/29/09
to pon...@googlegroups.com


2009/12/29 sagasw <sag...@gmail.com>
公式都是给的刷刷的,
貌似写到实际代码就有些费劲了



我给出答案了:)
 

Mikster.Z

unread,
Dec 29, 2009, 1:33:19 AM12/29/09
to pon...@googlegroups.com
没有一个给出完整答案的,看来是有些难。

公式都是给的刷刷的,
貌似写到实际代码就有些费劲了

我Copy Paste 比您打字效率高多了~~ :)
2009/12/29 sagasw <sag...@gmail.com>

Shuo Chen

unread,
Dec 29, 2009, 1:37:39 AM12/29/09
to TopLanguage
你这个
print can_m2k(100, 4950)
要多长时间?

print can_m2k(500, 10000)
呢?

On Dec 29, 2:20 pm, 张沈鹏 <zsp...@gmail.com> wrote:
> 2009/12/29 Shuo Chen <giantc...@gmail.com>

sagasw

unread,
Dec 29, 2009, 1:58:31 AM12/29/09
to pon...@googlegroups.com
看到这上面的图,我忽然悟了

http://www.cs.sunysb.edu/~algorith/files/generating-partitions.shtml

实现需要点时间,不过应该是能写出来了。


------------------------------------
C++, Lua, living in Dalian
http://sunxiunan.com/
http://twitter.com/sagasw
------------------------------------


2009/12/29 Shuo Chen <gian...@gmail.com>

张沈鹏

unread,
Dec 29, 2009, 2:01:09 AM12/29/09
to pon...@googlegroups.com


2009/12/29 Shuo Chen <gian...@gmail.com>

你这个
print can_m2k(100, 4950)
要多长时间?

print can_m2k(500, 10000)
呢?

 
题目条件中


2<=N<=100, 1 <= K <= 10000.


写这样一个for循环

for m in range(1,101):
    a = get_all_k(m)
    print "#%s\n%s,"%(m,a)

执行

Zues:work ~:python te.py > reuslt.py&

这样的命令

生成一个python静态化的文件就好了

比什么算法都快

另外我不喜欢你这种语气。

凡事够用就好,无需追求完美。

Hongzhang Liu

unread,
Dec 29, 2009, 2:15:08 AM12/29/09
to pon...@googlegroups.com
我给一个答案吧:

def max_square_sum(n, k):
    return k - 1 + (n - k + 1) * (n - k + 1)

def min_square_sum(n, k):
    avg = n / k
    remain = n - (k - 1) * avg
    return (k - 1) * avg * avg + remain * remain

def possible(n, k, s):
    if s > max_square_sum(n, k):
        return False
    if s < min_square_sum(n, k):
        return False
    return True

def find(n, k, s):
    if k == 1:
        if s == n * n:
            return [n]
        else:
            return None

    avg = n / k
    for i in xrange(n - k + 1, avg - 1, -1):
        s_r = s - i * i
        if possible(n - i, k - 1, s_r):
            ret = find(n - i, k - 1, s_r)
            if ret is not None:
                ret.append(i)
                return ret

def fight(N, K):
    s = N * N - 2 * K
    for i in xrange(2, N + 1):
        if possible(N, i, s):
            ret = find(N, i, s)
            if ret is not None:
                print ret
                return 'YES'
            
    return 'NO'

在python shell里运行,不论是fight(100, 4950)还是fight(500, 10000)都是瞬间完成

平方和的最大值与最小值可以过滤掉很多划分。

2009/12/29 张沈鹏 <zsp...@gmail.com>

Shuo Chen

unread,
Dec 29, 2009, 2:26:16 AM12/29/09
to TopLanguage
fight(100, 483) 你的答案是 NO。
但是分组 1, 2, 2, 95 满足条件。

> > 2009/12/29 Shuo Chen <giantc...@gmail.com>

Hongzhang Liu

unread,
Dec 29, 2009, 2:53:31 AM12/29/09
to pon...@googlegroups.com
min计算错误,对于(5, 3)这种划分算得不对。

修改了一下:
def max_square_sum(n, k):
    return k - 1 + (n - k + 1) * (n - k + 1)

def min_square_sum(n, k):
    avg = n / k
    return k * avg * avg

def possible(n, k, s):
    if s > max_square_sum(n, k):
        return False
    if s < min_square_sum(n, k):
        return False
    return True

def find(n, k, s):
    if k == 1:
        if s == n * n:
            return [n]
        else:
            return None

    avg = n / k
    for i in xrange(n - k + 1, avg - 1, -1):
        s_r = s - i * i
        if s_r <= 0:
            continue
        if possible(n - i, k - 1, s_r):
            ret = find(n - i, k - 1, s_r)
            if ret is not None:
                ret.append(i)
                return ret
            else:
                return None

def fight(N, K):
    s = N * N - 2 * K
    for i in xrange(2, N + 1):
        if possible(N, i, s):
            ret = find(N, i, s)
            if ret is not None:
                print ret
                return 'YES'
            
    return 'NO'

另外,如果有测试集的话不妨发上来

2009/12/29 Shuo Chen <gian...@gmail.com>

Shuo Chen

unread,
Dec 29, 2009, 3:05:11 AM12/29/09
to TopLanguage
fight(100, 1014) 结果不对,你算出 NO
分组 1 5 5 89 满足条件。

测试集是 fight(100, 1) 到 fight(100, 5000) 的结果,有5000行,怎么发?
要不我把我的代码发上来?

> 2009/12/29 Shuo Chen <giantc...@gmail.com>

Hongzhang Liu

unread,
Dec 29, 2009, 3:16:21 AM12/29/09
to pon...@googlegroups.com
去掉红色的两行...

2009/12/29 Shuo Chen <gian...@gmail.com>

Shuo Chen

unread,
Dec 29, 2009, 6:11:15 AM12/29/09
to TopLanguage
喜不喜欢我的语气没有关系。

一个指数时间的算法如何称得上"够用就好"?
can_m2k(100,4950) 在一台X5150上算了10分钟没有出结果。
can_m2k(40, 1), can_m2k(50, 1), can_m2k(60, 1), can_m2k(61, 1), can_m2k
(70,1) 的计算时间分别是:
0.8s,
5.0s,
28s,
34.4s,
140s
大致可以看出,人数每增加10,时间是5倍。

On Dec 29, 3:01 pm, 张沈鹏 <zsp...@gmail.com> wrote:
> 2009/12/29 Shuo Chen <giantc...@gmail.com>
>

Alecs King

unread,
Dec 29, 2009, 7:43:08 AM12/29/09
to pon...@googlegroups.com
On Mon, Dec 28, 2009 at 05:38:19AM -0800, Shuo Chen wrote:
> http://www.javaeye.com/topic/88204
>
> 前面那道题太简单了,这里来一道稍微难点的。
>
> 有 N 个人,分成 M 组(2<=M<=N)打比赛,每组内部不竞争,每个人和不在同一组的其他人每人打一场。
>
> 比如有ABCD四个人,如果分2组,{ABC}{D}要打3场,{AB}{CD}打4场,
> 分3组{A}{B}{CD}打5场,分4组{A}{B}{C}{D}打6场。显见没有哪种分组能只打1场。
>
> 问题:有 N 个人,有没有哪种分组恰好打 K 场比赛?
>
> bool fight(int N, int K);
>
> 2<=N<=100, 1 <= K <= 10000.

#!/usr/bin/python

def memo(func):
cache = {}
def f(*args):
if args in cache:
return cache[args]
r = func(*args)
cache[args] = r
return r
return f

@memo
def ok(n, k):
if k == 0 and n > 0: return True
if k < 0 or n < 0 or 2*k > n**2: return False
for x in xrange(1, n+1): # last
if ok(n-x, k-x*(n-x)): return True
return False

while True:
try:
n, k = map(int, raw_input().strip().split())
except EOFError:
break
print ok(n, k)

--
Alecs King

Shuo Chen

unread,
Dec 29, 2009, 9:27:36 AM12/29/09
to TopLanguage
举一个例子,简单说一下思路,有 100 个人,问能不能打 1000 场。

这个问题无法直接下手,但是可以考虑下面这些情况:
1. 如果把其中某 1 个人分为一组,他和剩下的 99 个人肯定要打 99 场比赛,不管这 99 个人如何分组。
那么问题规模缩小,转化为 99 个人能不能打 1000-99=901 场比赛。如果能打,100个人就能打1000场。
2. 如果把其中某 2 个人分为一组,他和剩下的 98 个人肯定要打 98*2 场比赛,不管这 98 个人如何分组。
那么问题规模缩小,转化为 98 个人能不能打 1000-98*2 = 804 场比赛。如果能打,100个人就能打1000场。
2. 如果把其中某 3 个人分为一组,他和剩下的 97 个人肯定要打 97*3 场比赛,不管这 97 个人如何分组。
那么问题规模缩小,转化为 97 个人能不能打 1000-97*3 = 709 场比赛。如果能打,100个人就能打1000场。
......
99. 如果把其中某 99 个人分为一组,他和剩下的 1 个人肯定要打 99 场比赛,不管这 1 个人如何分组。
那么问题规模缩小,转化为 1 个人能不能打 1000-99 = 901 场比赛。如果能打,100个人就能打1000场。

于是很容易写出递归算法,再加上把算过的结果缓存起来,结果就出来了。

#include <stdio.h>
#include <stdlib.h>

const int MAX_N = 500;
bool impossible[MAX_N+1][MAX_N*MAX_N/2];

bool baidu(int people, int fights)
{
if (fights == 0)
return true;
if (fights < 0 || (people*(people-1)/2) < fights)
return false;
if (impossible[people][fights])
return false;

for (int p = 1; p < people; ++p) {
if(baidu(people - p, fights - p*(people-p)))
return true;
}

impossible[people][fights] = true;
return false;
}

int main(int argc, char* argv[])
{
int n = atoi(argv[1]);
int k = atoi(argv[2]);
printf("%s\n", baidu(n, k) ? "YES" : "NO");

Jacky, Z

unread,
Dec 29, 2009, 9:39:09 AM12/29/09
to pon...@googlegroups.com
楼主给10组左右的数据来测试一下,方便我们找找bug嘛。
我来试试
////---------------------------------------------------------
#include <stdio.h>
#include <math.h>

bool grouprace(int N, int k) {
    if ( N<2 || k<0 || k>N*(N-1)/2 || N==1 || k<N-1 )
        return false;
    if ( k==0 || k==N-1 || k==N*(N-1)/2)
        return true;
    int max_race = N*(N-1)/2;
    while (max_race >= k && N>=0) {
        int merge = (int)sqrt(2.0*(max_race-k));
        if (merge*(merge+1) <= 2*(max_race-k))
            merge += 1;
        if ( merge<=1 && N>=2 ) {
            return true;
        } else {
            max_race -= merge*(merge-1)/2;
            if (max_race == k) {
                return true;
            } else if(max_race < k)
                return false;
            N -= merge;
        }
    }
    return false;
}

int main() {
    if (grouprace(100, 1014))
        printf("TRUE\n");
    else
        printf("FALSE\n");;
}

2009/12/28 Shuo Chen <gian...@gmail.com>

Jacky, Z

unread,
Dec 29, 2009, 10:06:19 AM12/29/09
to pon...@googlegroups.com
我错了, 我悔过

2009/12/29 Jacky, Z <fans...@gmail.com>
323.gif

Jacky, Z

unread,
Dec 29, 2009, 10:14:56 AM12/29/09
to pon...@googlegroups.com
这题感觉应该有比DP跟高效的算法。

2009/12/29 Shuo Chen <gian...@gmail.com>

Jacky, Z

unread,
Dec 29, 2009, 11:12:54 PM12/29/09
to pon...@googlegroups.com
上面的搜索方式最终得回溯,直接的贪心算法得到的不是正确的解,最后也就相当于把楼主的这个来做一个剪枝
程序未优化,性能也未测试
bool impossible2[MAX_N+1][MAX_N*MAX_N/2];

bool baidu2(int people, int fights)
{
    int fightmax = people*(people-1)/2;

    if (fights == 0)
           return true;
     if (fights < 0 || (fightmax) < fights)
           return false;
    if (impossible2[people][fights])
         return false;
    int max = (int)sqrt(2.0*(fightmax-fights));
    if (max*(max+1) <= 2*(fightmax-fights))
            max += 1;
    int min = (people*people-2*fights)/people;
    while(people/min*min*min+(people%min)*(people%min) > (people*people-2*fights))
        min--;
    for (int p = min; p <= max; ++p) {
           if(baidu2(people - p, fights - p*(people-p)))
             return true;
    }
     impossible2[people][fights] = true;
     return false;
}

2009/12/29 Shuo Chen <gian...@gmail.com>

Jacky, Z

unread,
Dec 29, 2009, 11:13:53 PM12/29/09
to pon...@googlegroups.com


2009/12/29 Jacky, Z <fans...@gmail.com>

Hongzhang Liu

unread,
Dec 30, 2009, 12:43:00 AM12/30/09
to pon...@googlegroups.com
嗯,我跑了一下,Shuo Chen的算法非常简单,但效率可能并不好。fight(100, 1)这种情况计算的复杂度就很高。

2009/12/30 Jacky, Z <fans...@gmail.com>

Shuo Chen

unread,
Dec 30, 2009, 1:10:58 AM12/30/09
to TopLanguage
小测验:
1、 baidu(100, 1) 会累计调用 baidu() 多少次?
2、 有没有 x 使得 baidu(100, x) 会调用 baidu() 超过 5 万次?

参考答案:
1. 100 次
2. x = 1795 等数。

思考题:对于 1<=x<=4950,baidu(100,x) 的平均调用次数是多少,中位数是多少?

On Dec 30, 1:43 pm, Hongzhang Liu <hongzhang....@gmail.com> wrote:
> 嗯,我跑了一下,Shuo Chen的算法非常简单,但效率可能并不好。fight(100, 1)这种情况计算的复杂度就很高。
>

> 2009/12/30 Jacky, Z <fanste...@gmail.com>
>

张沈鹏

unread,
Dec 30, 2009, 1:20:51 AM12/30/09
to pon...@googlegroups.com
我忽然发现我在
2007-5-13
做过这条题目
代码见附件
那时还爱着C++,如今却弃若敝屣
时光如梭,恍如隔世
test.zip

张沈鹏

unread,
Dec 30, 2009, 1:28:47 AM12/30/09
to pon...@googlegroups.com
2009/12/30 张沈鹏 <zsp...@gmail.com>:

不过貌似当时的答案是错的

Shuo Chen

unread,
Dec 30, 2009, 1:43:27 AM12/30/09
to TopLanguage
很不幸,是错的。
fight(100, 293) 可以分组为 1 2 97,而你的程序算出来是 NO.
fight(100, x) 为 YES 的情况有 3880 种 (x>=1),你算出了 1/10 不到。

> test.zip
> 3KViewDownload

Jacky, Z

unread,
Dec 30, 2009, 5:13:50 AM12/30/09
to pon...@googlegroups.com
Shuo Chen 不妨测试一下剪枝后性能怎么样。

2009/12/30 Shuo Chen <gian...@gmail.com>

Shuo Chen

unread,
Dec 30, 2009, 6:37:35 AM12/30/09
to TopLanguage
你做的优化还是你来测吧。

我发个递推版的,直接把表推出来:

bool possible[MAX_N+1][MAX_N*MAX_N/2];

void build(int people)
{
possible[people][0] = true;

for (int p = 1; p < people; ++p) {

int rem = people - p;
int add = p*rem;
int up = rem*(rem-1)/2;
for (int i = 0; i <= up; ++i) {
if (possible[rem][i]) // ***
possible[people][i+add] = true;
}
}
}

int main(int argc, char* argv[])
{
int n = atoi(argv[1]);

possible[1][0] = true;
for (int people = 2; people <= n; ++people)
build(people);
// print(n);
}

竞猜:代码中标有***那句的执行次数的big-O表示。候选答案:
1. O(n)
2. O(n**2)
3. O(n**3)
4. O(n**4)
5. O(n**5)
6. O(n * log n)
7. O(n * n * log n)

以上的 n 均为 main 中的 n。

On Dec 30, 6:13 pm, "Jacky, Z" <fanste...@gmail.com> wrote:
> *Shuo Chen *不妨测试一下剪枝后性能怎么样。
>
> 2009/12/30 Shuo Chen <giantc...@gmail.com>

张沈鹏

unread,
Dec 30, 2009, 8:50:00 AM12/30/09
to pon...@googlegroups.com
受我很久很久以前的C++代码启发,虽然的错误的:)
下班回来的公交上想到一个办法,回来5分钟写完,用这个生成静态文件不错

PRINT_N_CACHE = {}

def print_n(n):
if n<=1:
return (0,)
if n in PRINT_N_CACHE:
return PRINT_N_CACHE[n]

result = set((0,n-1))
for i in range(1,n):
offset = i*(n-i)
for j in print_n(i):
result.add(j+offset)
r = tuple(result)
PRINT_N_CACHE[n] = r
return r


print sorted(print_n(100))

张沈鹏

unread,
Dec 30, 2009, 9:18:37 AM12/30/09
to pon...@googlegroups.com
0-500的所有可能
result.7z

张沈鹏

unread,
Dec 30, 2009, 9:44:41 AM12/30/09
to pon...@googlegroups.com
附上cython代码,比python快一倍

Zues:work ~/test:cat setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("mkn", ["mkn.pyx"])]

setup(
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules
)

Zues:work ~/test:cat test.py
from mkn import print_n

for i in range(501):
print "%s,"%(",".join(map(str,sorted(set(print_n(i))))))
Zues:work ~/test:cat mkn.pyx

PRINT_N_CACHE = {}

def print_n(n):
if n<=1:
return (0,)
if n in PRINT_N_CACHE:
return PRINT_N_CACHE[n]

result = set((0,n-1))
for i in range(1,n):
offset = i*(n-i)
for j in print_n(i):
result.add(j+offset)
r = tuple(result)
PRINT_N_CACHE[n] = r
return r


Zues:work ~/test:python setup.py build_ext --inplace
running build_ext

Jacky, Z

unread,
Dec 30, 2009, 9:51:57 AM12/30/09
to pon...@googlegroups.com
好吧 我统计了一下对于n=100, k从1000 到10000得到结果如下
106ms 2652650
7ms 106292
后面那个是函数被调用的次数
2009/12/30 Shuo Chen <gian...@gmail.com>

asung0108

unread,
Jan 10, 2010, 9:02:44 PM1/10/10
to TopLanguage
大家觉得这个怎么样:
http://hi.csdn.net/link.php?url=http://blog.csdn.net%2FfirePhoenix1981

On Dec 30 2009, 1:43 pm, Hongzhang Liu <hongzhang....@gmail.com> wrote:

Reply all
Reply to author
Forward
0 new messages