/// <summary>
        /// Push/Pull 人力
        /// </summary>
        /// <param name="staffingStatistic">标的的SQ</param>
        /// <param name="required">短缺量,或过剩量有正负数</param>
        /// <param name="contributionHistory">记录贡献位置</param>
        /// <param name="insertRules">可安排的离线规则</param>
        /// <param name="startIndex">班表(Assignment)的起始</param>
        /// <param name="contributionLength">班表(Assignment)有效贡献长度</param>
        private void Contribute(IStaffingStatistic staffingStatistic, int required, List <List <Tuple <int, int, double> > > contributionHistory,
                                IEnumerable <SubEventInsertRule> insertRules, int startIndex, int contributionLength)
        {
            var isOverStaff = required < 0;
            var times       = Math.Abs(required);

            for (var i = 0; i < times; i++)
            {
                if (isOverStaff)
                {
                    staffingStatistic.Push(startIndex, contributionLength, -1);

                    if (contributionHistory.Count <= i)
                    {
                        continue;
                    }
                    foreach (var e in contributionHistory[i])
                    {
                        staffingStatistic.Push(e.Item1, e.Item2, 1);
                    }
                    contributionHistory.RemoveAt(i);
                }
                else // underStaff
                {
                    staffingStatistic.Push(startIndex, contributionLength, 1);

                    contributionHistory.Add(new List <Tuple <int, int, double> >());

                    foreach (var rule in insertRules.Where(r => !r.SubEvent.OnService))
                    {
                        //设定一个初始最大比对变量
                        var lowestScore = new Tuple <int, int, double>(-1, 0, int.MaxValue); // (离线事件startIndex),(离线事件长度),(离线事件在此时间短缺率)

                        var length = rule.SubEventLength / 5;                                //离线事件长度

                        var rounds = rule.GetPossibleMovementTimes();                        // 离线事件可以安排的可能位置
                        do
                        {
                            var occurIndex = startIndex + ((rule.TimeRange.StartValue + (rounds * rule.OccurScale)) / CellUnit); // 离线事件startIndex
                            var score      = staffingStatistic.GetShortfall(occurIndex, length);                                 // 离线事件在此时间短缺率
                            if (score < lowestScore.Item3)                                                                       // 愈小愈好
                            {
                                lowestScore = new Tuple <int, int, double>(occurIndex, length, score);
                            }
                            rounds--;
                        } while (rounds >= 0);

                        staffingStatistic.Push(lowestScore.Item1, length, -1); //lowestSorce.Item1 意思是最后胜出的离线事件startIndex
                        contributionHistory[i].Add(lowestScore);
                    }
                }
            }
        }
        /// <summary>
        /// Push/Pull 人力
        /// </summary>
        /// <param name="staffingStatistic">标的的SQ</param>
        /// <param name="required">短缺量,或过剩量有正负数</param>
        /// <param name="contributionHistory">记录贡献位置</param>
        /// <param name="insertRules">可安排的离线规则</param>
        /// <param name="startIndex">班表(Assignment)的起始</param>
        /// <param name="contributionLength">班表(Assignment)有效贡献长度</param>
        private void Contribute(IStaffingStatistic staffingStatistic, int required, List<List<Tuple<int, int, double>>> contributionHistory,
            IEnumerable<SubEventInsertRule> insertRules, int startIndex, int contributionLength)
        {
            var isOverStaff = required < 0;
            var times = Math.Abs(required);

            for (var i = 0; i < times; i++)
            {
                if (isOverStaff)
                {
                    staffingStatistic.Push(startIndex, contributionLength, -1);

                    if (contributionHistory.Count <= i)
                        continue;
                    foreach (var e in contributionHistory[i])
                        staffingStatistic.Push(e.Item1, e.Item2, 1);
                    contributionHistory.RemoveAt(i);
                }
                else // underStaff
                {
                    staffingStatistic.Push(startIndex, contributionLength, 1);

                    contributionHistory.Add(new List<Tuple<int, int, double>>());

                    foreach (var rule in insertRules.Where(r => !r.SubEvent.OnService))
                    {
                        //设定一个初始最大比对变量
                        var lowestScore = new Tuple<int, int, double>(-1, 0, int.MaxValue); // (离线事件startIndex),(离线事件长度),(离线事件在此时间短缺率)

                        var length = rule.SubEventLength / 5; //离线事件长度

                        var rounds = rule.GetPossibleMovementTimes(); // 离线事件可以安排的可能位置
                        do
                        {
                            var occurIndex = startIndex + ((rule.TimeRange.StartValue + (rounds * rule.OccurScale)) / CellUnit); // 离线事件startIndex
                            var score = staffingStatistic.GetShortfall(occurIndex, length); // 离线事件在此时间短缺率
                            if (score < lowestScore.Item3) // 愈小愈好
                                lowestScore = new Tuple<int, int, double>(occurIndex, length, score);
                            rounds--;
                        } while (rounds >= 0);

                        staffingStatistic.Push(lowestScore.Item1, length, -1); //lowestSorce.Item1 意思是最后胜出的离线事件startIndex
                        contributionHistory[i].Add(lowestScore);
                    }
                }
            }
        }