您的位置:首页 > 编程语言 > Java开发

SRM536-div1-2-RollingDiceDivOne

2012-03-17 22:17 302 查看
zz:
http://www.strongczq.com/2012/03/srm536-div1-2-rollingdicedivone.html

题目原文:
http://community.topcoder.com/stat?c=problem_statement&pm=11797&rd=14728

题目大意:
     有n个骰子,第i个骰子拥有dice[i]面,每面包含1,2,...,dice[i]的点数。同时掷这n个骰子,问掷出来的各个骰子的总点数是多少的概率最高,如果有多个答案,返回最小的那个。
     数据规模:n为[1,50],每个骰子的面数取值范围为[1,10^9]

思路:
     所谓的概率最高其实就是生成总点数的不同组合数最多。先用简单的例子发掘一下规律,假设两个骰子的面数分别是3和4,则总点数可以是2(1),
3(2), 4(3) ,5(3), 6(2), 7(1), 括号后面为该点数的组合数。可以发现可行总点数集合实际上是连续的整数,组合数先升后降,并且具有对称性。可以用下图来表示这些点数的生成方式,图中上面一行的数字表示4面骰子的各面点数,下面一行表示3面骰子的各面点数,从左到右分别表示了总点数从2到7的情况。我们发现每一种情况下上下对位的数字之和即是该情况所表示的总点数,而对位的个数就是组合数。图中从左到右可以看作是下面的三个数字组成的窗口逐一的往右移动生成的,所以就很容易理解为什么总点数的组合数会有先升后降以及对称性的规律。



     这里称组合数达到最大值时的总点数为极值点数。根据以上的分析,可以确定该题最终所有可能总点数的中间值肯定属于极值点数。但是题目要求拥有多个答案的时候返回最小的值,所以还需要考虑多个极值点数的情况。所以,解题的关键在于如何找到第一个极值点数。从上面的例子中可以看出如果两个骰子的大小不一致就会产生多个极值点数,但是多个骰子同时考虑时未必如此。上图中上面一行也可以看作是目前已考虑的骰子生成的总点数列表,每一个点数同时拥有一个组合数,下一行为新考虑的一个骰子。每当窗口对位一次,将窗口中包含的所有组合数相加,得到的值是一个新的总店数,添加到新的总点数列表中。那么,如果上一行的极值点数个数小于等于下一行的窗口长度,产生的新总点数列表里只会产生1或2个极值点数,否则会有多个。无论是那种情况,我们可以跟踪第一个极值点数的位置。为了符合图中上一行的长度大于等于下一行,我们需要先对所有骰子按面数排序。然后从大到小依次考虑。假设第一个极值点数的位置为pos
(0-based),当前总点数列表长度为len。则考虑第一个骰子时pos为0,len为其点数。从第二个骰子开始,比较骰子i的点数(窗口长度)与当前列表的极值点数个数(根据pos和对称性很容易计算):
窗口长度>=个数:则新的总点数列表只存在1或2个极值点数,由于对称性,极值点数必是新列表的中间点(或者中间两个),所以取pos=(len + dice[i] - 1)/2;

窗口长度<个数:则下一行窗口左侧正好移动到pos位置时产生第一个新的极值点数,所以pos=pos+dice[i]-1
完成以上操作后,更新len=len+dice[i]-1。
     遍历完所有骰子后,需要返回的值即为 “骰子数+pos”。由于只是一次线性遍历,所以算法时间复杂度为O(n)。

     Java代码:

public class RollingDiceDivOne {
public long mostLikely(int[] dice) {
Arrays.sort(dice);
long len = dice[dice.length - 1];
long maxPos = 0;
for (int i = dice.length - 2; i >= 0; --i) {
if(len - 2 * maxPos >= dice[i]){
maxPos += dice[i] - 1;
}else{
maxPos = (len + dice[i] - 2) / 2;
}
len += dice[i] - 1;
}
return dice.length + maxPos;
}
}


[align=left] 后记:看了官方题解后发现这题有更简洁的解题方法。假设n-1个面数最小的骰子生成了一个总点数列表。该列表中极值点数的个数肯定小于最后一个骰子的面数,那么考虑这个列表和最后的这个骰子:[/align]
如果列表的长度大于等于骰子的面数,则肯定只有1或2个新的极值点数,那么直接返回最终列表的中间值即可。
如果以上不成立,那么第一个新极值点数在最终列表中的位置肯定是“当前列表长度-1”。
  Java代码:
public class RollingDiceDivOne {
public long mostLikely(int[] dice) {
long sum = 0;
int max = 0;
for (int die : dice) {
sum += die - 1;
max = Math.max(max, die - 1);
}
return dice.length + Math.min(sum / 2, sum - max);
}
}


[align=left][/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  class java 算法