} // end of function CreateT_S_S_Network_db

        void initVirStartNode(ref List <T_S_S_Node> stateNodeList, ref Queue <T_S_S_Node> queue_T_S_S_Node)
        {
            foreach (T_S_Node base_start_point in base_start_pointList)
            {
                T_S_S_Node t_s_s_node = new T_S_S_Node();
                // 将起点放在编号前列,便于连续编号
                // 这段丑陋,先这样
                //foreach (CrewBase crewbase in CrewBaseList) {
                //    if (base_start_point.StaCode == crewbase.StaCode) {
                //        t_s_s_node.ID = crewbase.OIDinTSS;
                //        break;
                //    }
                //}
                t_s_s_node.ID = BaseStationToODIDDict[base_start_point.StaCode][0];

                t_s_s_node.Init_db(t_s_s_node.ID, -1,
                                   -1, 0, base_start_point.TrainCode,
                                   base_start_point.StaCode, base_start_point.TimeCode,
                                   0, base_start_point.StaCode,
                                   base_start_point.ID, -1,
                                   null, base_start_point, null);

                t_s_s_node.Resources = new Resource(LineList.Count);

                t_s_s_node.Price = 0;

                stateNodeList.Add(t_s_s_node);
                queue_T_S_S_Node.Enqueue(t_s_s_node);
            }
        }
        bool FeasibleDayCrewTime(T_S_S_Node curStateNode, T_S_Arc arc, ref Resource extendResource)
        {
            if (arc.ArcType == 31 || arc.ArcType == 33)
            {
                return(true);
            }

            int dayCrewTime_accumu = curStateNode.Resources.DayCrewTime_accumu;

            if (arc.ArcType != 32)
            {
                dayCrewTime_accumu += arc.Len;
            }

            if (dayCrewTime_accumu > BasicTimeRules.MaxDayCrewTime)
            {
                // 任何时候,dayCrewTime都不允许超过最大值
                return(false);
            }
            else if (arc.ArcType == 32 &&
                     (dayCrewTime_accumu < BasicTimeRules.MinDayCrewTime || dayCrewTime_accumu > BasicTimeRules.MaxDayCrewTime))
            {
                // 如果退乘时,dayCrewTime not in dayCrewTimeWindow [min, max],不可行
                return(false);
            }

            extendResource.SetDayCrewTime(dayCrewTime_accumu);

            return(true);
        }
        public void Init_db(int id, int prevID,
            int lineID, int routingID, string trainCode, 
            string station, int timeCode,
            int pointType, string OStation,
            int superID, int prevSuperID,
            T_S_S_Node prevNode,
            T_S_Node superPoint, T_S_Node prevSuperPoint) {

            this.iD = id;
            this.prevID = prevID;

            this.lineID = lineID;
            this.rountingID = routingID;
            this.trainCode = trainCode;

            this.staCode = station;
            this.timeCode = timeCode;

            this.pointType = pointType;
            this.OStation = OStation;

            this.superPointID = superID;
            this.prevSuperID = prevSuperID;

            this.PrevNode = prevNode;
            this.SuperPoint = superPoint;
            this.PrevSuperPoint = prevSuperPoint;
        }
        public static string stateNodePartialPathToStr(T_S_S_Node curStateNode, OutputMode mode)
        {
            StringBuilder strBuilder = new StringBuilder();

            if (mode == OutputMode.console)
            {
                strBuilder.AppendLine("----curStateNode's partial path info[Line]:");
            }

            if (curStateNode.PassLine.Count == 0)
            {
                strBuilder.Append("[Empty],");
            }
            else
            {
                strBuilder.Append("[");
                for (int i = 0; i < curStateNode.PassLine.Count - 1; i++)
                {
                    strBuilder.AppendFormat("{0}-", curStateNode.PassLine[i]);
                }
                strBuilder.AppendFormat("{0}],", curStateNode.PassLine.Last());
            }

            if (mode == OutputMode.console)
            {
                strBuilder.AppendLine("----curStateNode's partial path info[stateNode]:");
            }

            Stack <int> nodePahStack = new Stack <int>();
            var         preNode      = curStateNode;

            while (preNode != null)
            {
                nodePahStack.Push(preNode.ID);
                preNode = preNode.PrevNode;

                if (nodePahStack.Count > 10000)
                {
                    return(strBuilder.ToString());

                    throw new StackOverflowException("stateNodePartialPathToStr()死循环");
                }
            }
            if (nodePahStack.Count == 0)
            {
                strBuilder.AppendLine("[Empty]");
            }
            else
            {
                strBuilder.Append("[");
                while (nodePahStack.Count > 1)
                {
                    strBuilder.AppendFormat("{0}-", nodePahStack.Pop());
                }
                strBuilder.AppendFormat("{0}]", nodePahStack.Pop());
            }

            return(strBuilder.ToString());
        }
        // 处理用餐约束
        void handleMealConstraint(T_S_S_Node curStateNode, T_S_Arc arc, ref T_S_S_Node extendStateNode)
        {
            if (withinMealWindow(arc, STLunch, ETLunch, minMealTime))
            {
                if (/*curStateNode.Resources.LunchStatus < 2 &&*/
                    getRealMealSlot(arc, STLunch, ETLunch) < minMealTime)
                {
                    // 还未用过午餐,且当前弧应该用餐但无法用餐
                    // 则施加惩罚
                    extendStateNode.PenaltyMealViolate = arcPenaltyMealViolate;

                    extendStateNode.Resources.SetLunchStatus(curStateNode.Resources.LunchStatus);
                }
                else
                {
                    extendStateNode.Resources.SetLunchStatus(2);
                }
            }
            else if (withinMealWindow(arc, STDinner, ETDinner, minMealTime))
            {
                if (/*curStateNode.Resources.DinnerStatus < 2 &&*/
                    getRealMealSlot(arc, STDinner, ETDinner) < minMealTime)
                {
                    // 还未用过晚餐,且当前弧应该用餐但无法用餐
                    // 则施加惩罚
                    extendStateNode.PenaltyMealViolate = arcPenaltyMealViolate;

                    extendStateNode.Resources.SetDinnerStatus(curStateNode.Resources.DinnerStatus);
                }
                else
                {
                    extendStateNode.Resources.SetDinnerStatus(2);
                }
            }
            else
            {
                extendStateNode.Resources.SetLunchStatus(curStateNode.Resources.LunchStatus);
                extendStateNode.Resources.SetDinnerStatus(curStateNode.Resources.DinnerStatus);
            }
        }
        public static string stateNodeInfoToStr(T_S_S_Node curStateNode, OutputMode mode)
        {
            StringBuilder strBuilder = new StringBuilder();

            if (mode == OutputMode.console)
            {
                strBuilder.AppendLine("----curStateNode's basic info");
                strBuilder.AppendFormat("ID[{0}],NodeType[{1}],OStation[{2}],LineID[{3}],Train[{4}]," +
                                        "Station[{5}],Time[{6}],SuperPointID[{7}],SuperPointType[{8}],PrevNodeID[{9}]\n",
                                        curStateNode.ID, curStateNode.PointType, curStateNode.OStation,
                                        curStateNode.LineID, curStateNode.TrainCode,
                                        curStateNode.StaCode, curStateNode.TimeCode,
                                        curStateNode.SuperPointID, curStateNode.SuperPoint.PointType,
                                        curStateNode.PrevID);
                strBuilder.AppendLine("----curStateNode's state info");
                var resource = curStateNode.Resources;

                strBuilder.AppendFormat("DriveTime[{0}],DayCrewTime[{1}],Lunch[{2}],Dinner[{3}]\n",
                                        resource.DriveTime_accumu, resource.DayCrewTime_accumu,
                                        resource.LunchStatus, resource.DinnerStatus);
            }
            else if (mode == OutputMode.file)
            {
                strBuilder.AppendFormat("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},",
                                        curStateNode.ID, curStateNode.PointType, curStateNode.OStation,
                                        curStateNode.LineID, curStateNode.TrainCode,
                                        curStateNode.StaCode, curStateNode.TimeCode,
                                        curStateNode.SuperPointID, curStateNode.SuperPoint.PointType,
                                        curStateNode.PrevID);
                var resource = curStateNode.Resources;

                strBuilder.AppendFormat("{0},{1},{2},{3}",
                                        resource.DriveTime_accumu, resource.DayCrewTime_accumu,
                                        resource.LunchStatus, resource.DinnerStatus);
            }

            return(strBuilder.ToString());
        }
        // end 更新时间属性值

        void keepAllFeasibleStateNode(List <T_S_S_Node> allStateNodes)
        {
            T_S_S_NodeList = new List <T_S_S_Node>(allStateNodes);

            bool existIsolatedNode = false;

            do
            {
                existIsolatedNode = false;
                for (int i = T_S_S_NodeList.Count - 1; i >= 0; i--)
                {
                    T_S_S_Node node = T_S_S_NodeList[i];
                    if ((node.PointType == 32 || node.PointType == 33))
                    {
                        continue;
                    }
                    else if (node.Out_T_S_S_NodeList.Count == 0)
                    {
                        //Console.WriteLine(Logger.stateNodeInfoToStr(node, OutputMode.console));

                        node.PrevNode.Out_T_S_S_NodeList.Remove(node);
                        T_S_S_NodeList.RemoveAt(i);
                        existIsolatedNode = true;
                        continue;
                    }
                }
            } while (existIsolatedNode);

            // 重新编号
            // 按时间排序

            List <int> temp_debug = new List <int>()
            {
                2030, 2155, 2278, 2415, 2479, 2657, 2736, 2916
            };

            T_S_S_NodeList.Sort(T_S_S_Node_AscByTime.nodeAsc);
            int virODNum   = CrewBaseList.Count * 2;
            int newIDCount = virODNum;

            foreach (var node in T_S_S_NodeList)
            {
                if (node.ID >= virODNum)
                {
                    node.ID = newIDCount;
                    ++newIDCount;

                    //debug
                    //if (temp_debug.Contains(node.ID)) {
                    //    Console.WriteLine("cur node Price:" + node.Price);
                    //    Console.WriteLine("cur node PenaltyMealViolate:" + node.PenaltyMealViolate);
                    //}
                }
            }
            foreach (var node in T_S_S_NodeList)
            {
                if (node.PrevID >= 0)
                {
                    node.PrevID = node.PrevNode.ID;
                }
            }
        }
 /// <summary>
 /// arc为运行弧时使用
 /// 判断是否当前状态点(表示的部分路径)已经访问过该Line
 /// 避免同一Line被一条路径多次访问
 /// </summary>
 /// <param name="curStateNode"></param>
 /// <param name="arc"></param>
 /// <returns></returns>
 bool visitedCurLine(T_S_S_Node curStateNode, T_S_Arc arc)
 {
     return(curStateNode.Resources.LinesVisitedArray[arc.LineID - 1] > 0);
 }
        // end 处理用餐约束

        // 更新时间属性值,不涉及用餐约束
        bool FeasibleDriveTime(T_S_S_Node curStateNode, T_S_Arc arc, ref Resource extendResource)
        {
            if (arc.ArcType == 1)
            {
                //运行弧
                if (!visitedCurLine(curStateNode, arc))
                {
                    int driveTime_accumu = curStateNode.Resources.DriveTime_accumu + arc.Len;

                    if (driveTime_accumu <= BasicTimeRules.MaxDriveTime
                        /*&& dayCrewTime_accumu <= BasicTimeRules.MaxDayCrewTime*/)
                    {
                        // NOTE:只检查驾驶时间,不检查dayCrewTime

                        // 若执行该arc代表的Line后,驾驶时间 <= max,则可行
                        extendResource.SetDriveTime(driveTime_accumu);
                    }
                    else   // 若驾驶时间大于最大允许值,则不可extend,cut掉
                    {
                        return(false);
                    }
                }
            }
            else if (arc.ArcType == 21)
            {
                //日间连接弧
                int driveTime_accumu = curStateNode.Resources.DriveTime_accumu;

                if (BasicTimeRules.MinDriveTime <= driveTime_accumu &&
                    driveTime_accumu < BasicTimeRules.MaxDriveTime)
                {
                    // 1.若驾驶时间 in [min. max),则可行,但要判断是否可以间休

                    // NOTE:这里将最小间休时间等于最小接续时间
                    //     那么下面这个判断始终是true,即只要是接续弧,就视为间休,驾驶时间清零
                    if (arc.Len >= BasicTimeRules.MinConnTime)
                    {
                        // 若弧长 >= 最小间休时长,视为在此间休
                        extendResource.SetDriveTime(0);
                    }
                }
                else if (driveTime_accumu < BasicTimeRules.MinDriveTime)
                {
                    // 2.若驾驶时间 < 最小驾驶时间,则不间休,视为接续
                    extendResource.SetDriveTime(driveTime_accumu + arc.Len);
                }
                else if (driveTime_accumu == BasicTimeRules.MaxDriveTime)
                {
                    // 3.若驾驶时间 == 必须间休
                    if (arc.Len < BasicTimeRules.MinConnTime)
                    {
                        // 若弧长小于最小间休时长,则不可行
                        return(false);
                    }
                    // 否则,间休
                    extendResource.SetDriveTime(0);
                }
            }
            else if (arc.ArcType == 31 || arc.ArcType == 32 || arc.ArcType == 33)
            {
                //虚拟出乘弧, //虚拟退乘弧, //虚拟停驻弧
                if (arc.ArcType != 32)   //虚拟出乘,虚拟停驻,当前的状态点的驾驶时间都是0
                {
                    Debug.Assert(curStateNode.Resources.DriveTime_accumu == 0);
                    Debug.Assert(curStateNode.Resources.DayCrewTime_accumu == 0);
                }

                extendResource.SetDriveTime(curStateNode.Resources.DriveTime_accumu);
            }
            else
            {
                Debug.Print("检查驾驶时间约束,出现未定义的T_S_Arc.ArcType:{0}\n", arc.ArcType);
            }

            return(true);
        }
        public void CreateT_S_S_Network_db(/*CrewRule crewRule*/)
        {
            Debug.Assert(BasicTimeRules.MinConnTime != -1);

            List <T_S_S_Node>  allStateNodes = new List <T_S_S_Node>();
            Queue <T_S_S_Node> queue         = new Queue <T_S_S_Node>();

            initVirStartNode(ref allStateNodes, ref queue);

            //List<T_S_S_Node> tempDNodeList = new List<T_S_S_Node>();
            //Dictionary<int, T_S_S_Node> IDToNodeDict = new Dictionary<int, T_S_S_Node>();
            //foreach (var startNode in allStateNodes) {
            //    IDToNodeDict.Add(startNode.ID, startNode);
            //}

            int debug_count = 100000;
            int debug_iter  = 0;

            int        nodeCount = CrewBaseList.Count * 2; //其余时空状态点的编号从乘务基地虚拟起终点之后开始
            T_S_S_Node top_node;

            while (queue.Count > 0)
            {
                //if (--debug_count < 0) {
                //    debug_count = 100000;
                //    debug_iter++;

                //    Console.WriteLine("current Queue.Count is [{0}]", queue.Count);
                //    Console.WriteLine("debug iter is [{0}]", debug_iter);
                //}

                top_node = queue.Dequeue();
                //Console.WriteLine(Logger.stateNodeInfoToStr(top_node, OutputMode.console));
                //if (debug_iter >= 10) {
                //    if (debug_count  < 3000) {
                //        Console.WriteLine("cur debug count is [{0}]", debug_count);
                //    }
                //}
                T_S_Node       cur_super_point = top_node.SuperPoint;
                List <T_S_Arc> out_T_S_ArcList = cur_super_point.Out_T_S_ArcList;
                for (int i = 0; i < out_T_S_ArcList.Count; i++)
                {
                    T_S_Arc cur_t_s_arc = out_T_S_ArcList[i];


                    // debug
                    //if (top_node.PassLine.Contains(30)
                    //    && top_node.PassLine.Contains(143)
                    //    //&& top_node.PassLine.Contains(64)
                    //    //&& top_node.PassLine.Contains(157)
                    //    && cur_t_s_arc.EndTimeCode == 1029
                    //    && cur_t_s_arc.ArcType == 21) {
                    //    Console.WriteLine("top_node.PenaltyMealViolate:" + top_node.PenaltyMealViolate);
                    //}

                    //end debug

                    T_S_S_Node extendStateNode = new T_S_S_Node();
                    extendStateNode.Resources = new Resource(lineList.Count);

                    if (!FeasibleDriveTime(top_node, cur_t_s_arc, ref extendStateNode.Resources))
                    {
                        continue;
                    }
                    if (!FeasibleDayCrewTime(top_node, cur_t_s_arc, ref extendStateNode.Resources))
                    {
                        continue;
                    }
                    // 交路退乘点必须与出乘点相同 (必须回基地的约束)
                    if (cur_t_s_arc.ArcType == 32 &&
                        cur_t_s_arc.EndStaCode != top_node.OStation)
                    {
                        continue;
                    }
                    // 用餐软约束
                    // 必须是非运行弧,才可能可以用餐
                    if (cur_t_s_arc.ArcType != 1)
                    {
                        handleMealConstraint(top_node, cur_t_s_arc, ref extendStateNode);
                        // NOTE:可以处理一下,若当前弧用餐成功,则标记为用餐弧
                    }
                    else
                    {
                        extendStateNode.Resources.SetLunchStatus(top_node.Resources.LunchStatus);
                        extendStateNode.Resources.SetDinnerStatus(top_node.Resources.DinnerStatus);
                    }

                    // extendStartNode基本属性赋值
                    extendStateNode.Init_db(nodeCount, top_node.ID,
                                            cur_t_s_arc.LineID, cur_t_s_arc.RountingID, cur_t_s_arc.TrainCode,
                                            cur_t_s_arc.EndStaCode, cur_t_s_arc.EndTimeCode,
                                            cur_t_s_arc.ArcType, top_node.OStation,
                                            cur_t_s_arc.EndPointID, cur_super_point.ID,
                                            top_node, cur_t_s_arc.EndPoint, cur_super_point);

                    // 还有其他属性
                    // 1 途径运行线
                    extendStateNode.PassLine.AddRange(top_node.PassLine);
                    if (cur_t_s_arc.ArcType == 1)
                    {
                        extendStateNode.PassLine.Add(cur_t_s_arc.LineID);
                        extendStateNode.Resources.LinesVisitedArray[cur_t_s_arc.LineID - 1] += 1;
                    }
                    // 2 起终点的编号
                    if (cur_t_s_arc.ArcType == 31)
                    {
                        extendStateNode.Price = 1440;

                        Debug.Assert(extendStateNode.PenaltyMealViolate == 0);
                    }
                    else if (cur_t_s_arc.ArcType == 32)
                    {
                        extendStateNode.ID    = BaseStationToODIDDict[extendStateNode.StaCode][1];
                        extendStateNode.Price = 0;
                        --nodeCount; // 终点有自己的编号,所以此次不用增加nodeCount;
                        Debug.Assert(extendStateNode.PenaltyMealViolate == 0);
                    }
                    else if (cur_t_s_arc.ArcType == 33)
                    {
                        extendStateNode.ID    = BaseStationToODIDDict[extendStateNode.StaCode][1];
                        extendStateNode.Price = virtualRoutingCost;
                        --nodeCount; // 终点有自己的编号,所以此次不用增加nodeCount;
                        Debug.Assert(extendStateNode.PenaltyMealViolate == 0);
                    }
                    else
                    {
                        extendStateNode.Price = cur_t_s_arc.Len;// + extendStateNode.PenaltyMealViolate;
                        //后续对弧再赋值Penalty
                    }


                    // 比较标号优劣
                    //for (int stateIdx = cur_t_s_arc.EndPoint.StateNodeSet.Count; stateIdx >= 0; stateIdx--) {
                    //}

                    top_node.Out_T_S_S_NodeList.Add(extendStateNode);
                    allStateNodes.Add(extendStateNode);
                    queue.Enqueue(extendStateNode);
                    ++nodeCount;

                    //if (cur_t_s_arc.ArcType != 32 && cur_t_s_arc.ArcType != 33) {
                    //    IDToNodeDict.Add(extendStateNode.ID, extendStateNode);
                    //}
                    //else {
                    //    tempDNodeList.Add(extendStateNode);
                    //}
                } // end of loop "out_T_S_ArcList"
            }     // end of loop "while"

            // 找出所有可行的状态点(即可以到达虚拟终点的状态点)

            #region //基于回溯
            //T_S_S_NodeList = new List<T_S_S_Node>();
            //foreach (T_S_S_Node note in tempDNodeList) {
            //    int d = note.PrevID;
            //    do {

            //        if (IDToNodeDict.ContainsKey(d)) {
            //            IDToNodeDict[d].Isfeasible = 1;
            //            T_S_S_NodeList.Add(IDToNodeDict[d]);

            //            d = IDToNodeDict[d].PrevID;
            //        }
            //    }
            //    while (d != -1);
            //}
            //T_S_S_NodeList.AddRange(tempDNodeList);
            //T_S_S_NodeList.Sort(T_S_S_Node_AscByTime.nodeAsc);
            //int virODNum = CrewBaseList.Count * 2;
            //int newIDCount = virODNum;
            //foreach (var node in T_S_S_NodeList) {
            //    if (node.ID >= virODNum) {
            //        node.ID = newIDCount;
            //        ++newIDCount;
            //    }
            //}
            //foreach (var node in T_S_S_NodeList) {
            //    if (node.PrevID >= 0) {
            //        node.PrevID = node.PrevNode.ID;
            //    }
            //}
            #endregion

            keepAllFeasibleStateNode(allStateNodes);

            // 根据可行状态点建立时空状态弧
            createT_S_S_Arcs();
        } // end of function CreateT_S_S_Network_db
        // 创建时空状态弧
        void createT_S_S_Arcs()
        {
            T_S_S_ArcList = new List <T_S_S_Arc>();
            for (int i = 0; i < T_S_S_NodeList.Count; i++)
            {
                // NOTE:i应该从虚拟起点后开始
                T_S_S_Node curNode = T_S_S_NodeList[i];
                if (curNode.PointType == 0)
                {
                    continue;
                }
                T_S_S_Node prevNode = curNode.PrevNode;

                T_S_S_Arc t_s_s_arc = new T_S_S_Arc();
                t_s_s_arc.ID           = T_S_S_ArcList.Count;
                t_s_s_arc.StartPointID = prevNode.ID;
                t_s_s_arc.EndPointID   = curNode.ID;

                t_s_s_arc.StartNode = prevNode;
                t_s_s_arc.EndNode   = curNode;


                if (curNode.PointType == 31)
                {
                    Debug.Assert(prevNode.TimeCode == 0);
                }
                else if (curNode.PointType == 32)
                {
                    Debug.Assert(curNode.TimeCode == 1440);
                }
                else if (curNode.PointType == 33)
                {
                    Debug.Assert(prevNode.TimeCode == 0 && curNode.TimeCode == 1440);
                }
                t_s_s_arc.StartTimeCode = prevNode.TimeCode;
                t_s_s_arc.EndTimeCode   = curNode.TimeCode;

                t_s_s_arc.NumSelected = 0;
                t_s_s_arc.LineID      = curNode.LineID;
                t_s_s_arc.TrainCode   = curNode.PointType == 1 ? curNode.TrainCode : "";
                t_s_s_arc.ArcType     = curNode.PointType;

                // Price
                t_s_s_arc.SetPenaltyMealViolate(curNode.PenaltyMealViolate);
                t_s_s_arc.SetArcPrice(curNode.Price, curNode.PenaltyMealViolate);
                t_s_s_arc.LagPrice  = t_s_s_arc.Price;
                t_s_s_arc.HeurPrice = t_s_s_arc.Price;

                if (t_s_s_arc.ArcType == 1 || t_s_s_arc.ArcType == 33)
                {
                    t_s_s_arc.LagMultiplier = 0;
                }
                else
                {
                    t_s_s_arc.LagMultiplier = -1;
                }
                // meal status
                t_s_s_arc.EndCumuLunch  = curNode.Resources.LunchStatus;
                t_s_s_arc.EndCumuDinner = curNode.Resources.DinnerStatus;

                T_S_S_ArcList.Add(t_s_s_arc);
            }
        }