zoukankan      html  css  js  c++  java
  • 2014编程之美资格赛 第二题 大神和三个小伙伴

    解题思路:

    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
    ", 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作为差分的逆运算,对这个公式进行逆向回推,可以得到与统计分析一模一样的结论。

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

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

    所以在技术细节上

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

  • 相关阅读:
    Dubbo简介---搭建一个最简单的Demo框架
    git学习总结
    FastJson对于JSON格式字符串、JSON对象及JavaBean之间的相互转换
    Spring AOP实现Mysql数据库主从切换(一主多从)
    Mybatis中int insertSelective()的相关问题
    主从数据库读写分离知识
    IoC理解
    AOP理解
    MyBatis中mybatis-generator代码生成的一般过程
    fread 快速读入
  • 原文地址:https://www.cnblogs.com/RohanVon/p/3662212.html
Copyright © 2011-2022 走看看