即将举行选举,每个政党都会为各个选区指定一名候选人参加该选区角逐,选举规则非常苛刻,规定只有当一个党派获胜的选区数大于其他所有党派的选区数之和时,这个党派才被认为赢得选举。各选区的人数统计在一个数组中,求能够充分确保一个党派获胜的最小人数。
例:选区人数districts={8,10,12},result=21,即当该党赢得21人时,可以确保获胜,比如voters={4,5,12},无论如何调整该党获胜选区数都大于其他党派之和。
不妨先设各党派赢得一个选区获1.0积分,输掉一个选区获0分,打平双方各获0.5积分,当积分大于districts.length/2.0时获胜。
从获胜党派的角度出发,是关于获胜党在赢得n个选民时所能获得的最少积分数的问题,第一个使得己方积分达到获胜积分的选民数n即为解。
从落选党派的角度出发,则是关于落选党如何最合理的利用有限的选民数n来最大化自己的积分的问题,第一个使得己方积分大于或等于获胜积分的n即为落选党所需的能够牵制对方不让其获胜的最少人数,设为min,如果反对党只获得了min-1个选民,则获胜党可以确保胜利,即当获胜党获得(总人数-min+1)个选民时它必定获胜。
实现想到的是用二维浮点数组score[i][n],记录前i个选区n个选民时的得分。如果是获胜党派的积分,则表示该党在前i个选区获得n个选民时的最小可能积分;如果是落选党派的积分,则表示这些党派在前i个选区一共获得n个选民时的最大可能积分。以后一种情况为例迭代过程:
在第i次迭代完成后,进入第i+1次迭代,设胜负人数分水岭threshold=(districts[i+1]+1)/2,是否能平局drawable=(districts[i+1]%2==0)
{ score[i][n] (initial)
{ score[i][k] (0<k<n && k<threshold)
score[i+1][n] = max { score[i][k]+0.5 (0<k<n && k==threshold && drawable)
{ score[i][k]+1 (0<k<n && (k==threshold && !drawable || k> threshold))
人数n的范围可以以总人数的1/2为限,遍历score[districts.length][...]找出第一个达到获胜积分的人数min,然后解就是(总人数-min+1)。如果从获胜党的角度解,人数n的上限是总人数,求解规模变大了,然后初始化时有些差异,迭代过程类似,区别只是max()操作变成了min()操作
上述解是一个可行解,但是残酷的系统测试显示在一些测试用例上计算超时了,问题在于总人数的一半有时还是一个较大的值。
【2010-03-27更新】
经过进一步分析score[i][j]的数据秩序,发现第三层的for循环由简单的判断替换,大大提高执行效率,顺利通过了所有系统测试用例。
最后转一个大牛rng_58的三维动态规实现,仰慕中……拷下来以供学习,呵呵
【转自rng_58的实现(java版)】