导航

2014编程之美资格赛 第二题 大神和三个小伙伴

Posted on 2014-04-13 14:27  Rohan  阅读(371)  评论(2编辑  收藏  举报

解题思路:

  1. 暴搜获得Raw Data
  2. 找出规律,通项表达式

题目说明:

时间限制:2000ms

单点时限:1000ms

内存限制:256MB

描述

L国是一个有着优美景色且物产丰富的国家,很多人都喜欢来这里旅游并且喜欢带走一些纪念品,大神同学也不例外。距离开L国的时间越来越近了,大神同学正在烦恼给她可爱的小伙伴们带什么纪念品好,现在摆在大神同学面前的有三类纪念品A, B, C可以选择,每类纪念品各有N种。其中种类为A_i, B_i, C_i的纪念品价值均为i, 且分别有N+1-i个剩余。现在大神同学希望在三类纪念品中各挑选一件然后赠送给她的三名可爱的小伙伴,但是她又不希望恰好挑出来两件价值相同的纪念品,因为这样拿到相同价值纪念品的两位小伙伴就会认为大神同学偏袒另一位小伙伴而不理睬她超过一星期。现在,大神同学希望你买到的三件纪念品能让三位小伙伴都开心并且不和她闹别扭,她想知道一共有多少种不同挑选的方法?

因为方案数可能非常大,大神同学希望知道挑选纪念品的方案数模10^9+7之后的答案。

 

输入

第一行包括一个数T,表示数据的组数。

接下来包含T组数据,每组数据一行,包括一个整数N。

输出

对于每组数据,输出一行“Case x: ”,其中x表示每组数据的编号(从1开始),后接一个数,表示模10^9+7后的选择纪念品的方案数。

 

数据范围

小数据:

1<=T<=10

1<=N<=100

大数据:

1<=T<=1000

1<=N<=10^18

 

样例解释

对于第二组数据,合法的方案有以下几种,(X,Y,Z)表示选择了A类纪念品中价值为X的,B类纪念品中价值为Y的,C类纪念品中价值为Z的。

(1,1,1): 3*3*3=27种

(1,2,3): 3*2*1=6种

(1,3,2): 3*1*2=6种

(2,1,3): 2*3*1=6种

(2,2,2): 2*2*2=8种

(2,3,1): 2*1*3=6种

(3,1,2): 1*3*2=6种

(3,2,1): 1*2*3=6种

(3,3,3): 1*1*1=1种

一共27+6+6+6+8+6+6+6+1=72种选择纪念品的方案

注意,如(1,1,2), (2,3,3), (3,1,3)都因为恰好选择了两件价值相同的纪念品,所以并不是一种符合要求的纪念品选择方法。

 

 

 

样例输入

2

1

3

样例输出

Case 1: 1

Case 2: 72


解决思路

 1.暴力解决

暴搜的代码:

这个很简单,直接遍历所有可能的价值组合,并计算每种价值组合的子选择组合。

直接用暴力破解的时间复杂度为O(n^3) 。很明显,对于题目要求的输入规模范围,直接用暴力是不可行的。

 1 for (int turn_count = 0; turn_count < T; turn_count++)
 2     {
 3         sum = 0;
 4         N = turns[turn_count];
 5         
 6         for (int i = 1; i <= N; i++)
 7         {
 8             for (int j = 1; j <= N; j++)
 9             {
10                 for (int k = 1; k <= N; k++)
11                 {
12                     if ((i == j || j == k || k == i) && !(i == j && j == k))
13                     {//if exists two equal value ,then break.
14                         continue;
15                     }
16                     sum += count(i, j, k);
17                     result = sum % (1000000007);
18                 }
19             }
20 
21         }
22 
23         printf("Case %d: %d\n", turn_count+1, (int)sum);
24     }


然后获得一些Raw Data,为下一步求得通项做准备。

这是一组原始数据:

1

1

2

9

3

72

4

400

5

1575

6

4851

7

12544

8

28512

9

58725

10

111925

11

200376

12

340704

13

554827

14

870975

15

1324800

16

1960576

17

2832489

18

4006017

19

5559400

20

7585200

 

2.求得通项

最简单的方法:采用统计学方法。

由数学直觉,可以推断这是一个多项式的通项。

直接用Matlab执行 polyfit(x,y,10);

拿一个十次多项式拟合。果然,得到了很漂亮的数据:

一秒解决问题。

   2        2   2
  n  (n + 1)  (n  - 3 n + 4)
  --------------------------
              8                                                 

但是,这么搞很明显没有理论支持。因此有必要对整个过程进行一次分析:

结论就摆在这里了,对原理没有兴趣的同学可以直接略过了。

 

1

1

1

0

0

0

0

0

 

2

9

9

0

0

0

0

0

 

3

72

36

36

6

6

2

2

12

4

400

100

300

50

44

11

9

13 23

5

1575

225

1350

225

175

35

24

14 24 34

6

4851

441

4410

735

510

85

50

15 25 35 45

7

12544

784

11760

1960

1225

175

90

16 26….56

8

28512

1296

27216

4536

2576

322

147

 

9

58725

2025

56700

9450

4914

546

224

 

10

111925

3025

108900

18150

8700

870

324

 

11

200376

4356

196020

32670

14520

1320

450

 

12

340704

6084

334620

55770

23100

1925

605

 

13

554827

8281

546546

91091

35321

2717

792

 

14

870975

11025

859950

143325

52234

3731

1014

 

15

1324800

14400

1310400

218400

75075

5005

1274

 

16

1960576

18496

1942080

323680

105280

6580

1575

 

17

2832489

23409

2809080

468180

144500

8500

1920

 

18

4006017

29241

3976776

662796

194616

10812

2312

 

19

5559400

36100

5523300

920550

257754

13566

2754

 

20

7585200

44100

7541100

1256850

336300

16815

3249

 

N

实际结果

(x,x,x)

三个不同的组合数

除6修正

增量

增量分析:因子

项数

正向分析:

这个问题可以仍然抽象为概率论中通用的球和盒子的问题形式。

可以理解为三个球,放入N个盒子中。 且编号为i的盒子里面有N+1-i个格子。一个格子可以放多个球。

约束条件为:一个盒子内不可以恰好有两个球;问这三个球的不同放法。

 

为了解决这个问题,可以绘制一张表格:

第一列:N的序号

第二列:暴力计算得到的原始数据

 

第一步,放法可以划分为两个子类:三个球放入同一个盒子中,三个球放入三个不同的盒子中。对于第一种情况:可能的情况种类是容易计算的,即,放入同一个盒子i的可能组合有i^3种。那么总共的可能性就是i^3 从1到N的级数和(n(n+1)/2)^2 。得到的结果放在第三列

 

第二步,考虑三个球放入三个不同盒子中的情况。

用暴力算法得到的数据,减去放入同一个盒子的组合数,得到放入不同盒子的组合数。结果放入第四列

 

第三步,我们可以发现,ABC三者的次序对于本问题研究并无影响,因此对排列数进行废序处理。

除以3! 。得到无序的子总体个数,放入第五列。

 

第四步,当从N=k-1过度到N=k时,我们对后者中的组合情况进行一次划分:{选择了价值为k物品的组合,和没有选择价值k物品的组合}。前者恰好是从N=k-1到N=k的增量,我们需要关注增量,因此对第五列的数据差分后,在数据第一项前补0。结果放入第六列

 

第五步,我们发现第i项组合数,总是表示为i与一个数的乘积,是因为每种价值组合的子组合数计算公式:(N+1-i1)* (N+1-i2)* (N+1-i3)中,都含有因子i。因此,去除这个因子的影响。将结果放入第7列

 

第六步,我们终于可以发现直观的规律了。第八列的数据,我们可以转换为第九列的形式:

(1+…+N-2)*(N-1)

然后用Matlab级数求和命令symsum作为差分的逆运算,对这个公式进行逆向回推,可以得到与统计分析一模一样的结论。

 

这是这个问题的数学模型。

但是很操蛋的是,测试数据非常大。

所以在技术细节上

这个公式的计算有着巧妙的技巧....来避免溢出