华为2017软件精英挑战赛 赛程回顾

2017华为软件挑战赛刚开始的时候,和队友花了一个月的时间开始弄,期间艰难困苦,有喜有悲,最好的时候排名到第一名,最差的也跑到倒数了,学习了很多东西,也知道自己很多东西还不会,写下留个纪念。
这个题目是在一定前提下,求最小费用流,我们的想法很简单,就是先找到每条最短路径,把所有消费节点看成一个超级汇点,然后随机确定服务器,找到每次分配费用流时候的最短路径,在该路径上添加最大流量,直到满足所有消费节点的流量位置,由于没有很好的确定服务器,导致最后在找服务器方面废了很多功夫,最后采取的策略是:先把所有消费节点放上服务器,然后随机选择若干服务器,一个一个删除,找到删除后能得到最低费用的服务器,然后删掉这个服务器,继续删,一直删到不能删为止,然后随机添加服务器,替换当前服务器,从而得到当前最优~
用过退火算法,但是可能没写好,比直接随机还差,最后直接随机了,这个题目最好是用其他算法,之前结构已经定性了,就没有改了,虽然有点遗憾,不过走到这里已经不遗憾了。
其他算法可以选择整数规划、单纯性、ZKW等其他最优解算法。现在比赛还没结束,所以就发初赛的代码吧,,,大家随便看看就行~
最让人失望的是:没近4强,竟然发99块钱的手环,真是low爆了,上去唱个歌都可以拿到的手环和音响就是99块钱,前四是手机,几千块,差距太大了,不得不说,真的low爆了,对华为很失望、车票也没报,而且办事效率太低,无语。

#include "deploy.h"
#include "stdio.h"
#include "stdlib.h"
#include "time.h"
#include <cstring>
#include <queue>
#include <iostream>
#include "math.h"
using namespace std;

#ifdef   WIN32   
#define   LOCALTIME_R(t)     localtime((t))   
#else   
#define   LOCALTIME_R(t)     localtime_r((t),   (struct   tm   *)&tmres)   
#endif   

#define _DEBUG

#define INLINE  static __inline
#ifdef _DEBUG
#define PRINT   printf
#else
#define PRINT(...)
#endif

#define MODE_LOW 200
#define MODE_Middle 500


#define UsingDistance
#define UsingRoom

bool IsUsingRoom = false;

struct GraphStruct CurrentGragh;                                                        //当前得到的表结构体形式
struct GraphStruct CurrentGragh_Temp;

unsigned short ConnectedLength_Min[Max_NetNodeNum][Max_NetNodeNum] = { 0 };             //定义任意两点之间最小距离
unsigned short ConnectedMin_ID[Max_NetNodeNum][Max_NetNodeNum] = { 0 }; //定义任意两点这间最小距离的ID链接,起始点,终止点,结果上一个的编号
unsigned short ConnectedLength_Min_Temp[Max_NetNodeNum][Max_NetNodeNum] = { 0 };                //定义任意两点之间最小距离
unsigned short ConnectedMin_ID_Temp[Max_NetNodeNum][Max_NetNodeNum] = { 0 };    //定义任意两点这间最小距离的ID链接,起始点,终止点,结果上一个的编号
bool IsInited = false;

unsigned short ExpensePointID[Max_NetNodeNum] = { 0 };  //消费节点绑定的ID号,比实际的ID号大1,实际操作的时候减1
unsigned short SumLength[Max_NetNodeNum][2] = { 0 };        //所有的消费节点距离普通节点的距离之和

short Path[Max_NetNodeNum][Max_LineNum][Max_LineNum] = { 0 };   //消费节点路径  一维:消费节点ID   二维:消费节点路径条数   三维:路径内容

unsigned short Path_Num[Max_NetNodeNum][2] = { 0 };                 //一维:消费节点ID   二维:0表示这是当前ID的第几条路径,1表示该路径有多少个点

unsigned short PathExpense[Max_NetNodeNum][Max_NetNodeNum] = { 0 };     //占用带宽   一维:消费节点ID   二维:占用带宽数
unsigned short UnsatifiedPoint[Max_NetNodeNum] = { 0 };//当前网络不能满足的点,编号+1,实际操作时减1
unsigned short UnsatifiedNum = 0;                       //未满足的网络编号数量
bool IsSatified = false;
unsigned short SelectedServerNum = 1;                   //选中节点的数量   
unsigned short NetPathALL = 0;                      //总路径条数

unsigned short PreSearchedPath[Max_NetNodeNum] = { 0 };//上一次服务器编号
unsigned short PreServerNum = 0;                        //上一次服务器数量

unsigned short NowSearchedPath[Max_NetNodeNum] = { 0 };//当前服务器编号
unsigned short NowServerNum = 0;                        //当前服务器数量
unsigned short DeleteNum = 0;                           //删除的点号
unsigned long ChargeALL = 0;                            //所有费用计算
unsigned long PreChargeALL = 0;                         //所有费用计算
unsigned short Cant_Delete[Max_NetNodeNum] = { 0 };
unsigned int CantDeleteNum = 0;

unsigned short SinglePoint[Max_NetNodeNum] = { 0 };     //孤立节点编号
unsigned short SinglePointNum = 0;                      //孤立节点数量

unsigned short ConnectRelation[Max_NetNodeNum][Max_NetNodeNum] = { 0 }; //节点之间的链接关系
unsigned short ConnectNum[Max_NetNodeNum] = { 0 };      //与该节点的链接个数

unsigned short addnum[Max_NetNodeNum][2] = { 0 };   //服务器排序
unsigned short ExpenceClosedServer[Max_NetNodeNum][2] = { 0 };  //找消费节点最近的服务器

unsigned short BandWidth[TEXT_LineNum][4] = { 0 };  //0表示起始点,1表示终点,2表示流量,3表示单价
unsigned short BandWidthNum = 0;                        //普通节点文件行数

unsigned short ExpenseBandNeed[Max_NetNodeNum] = { 0 }; //消费节点需要的带宽

unsigned short ExpensePointSum[Max_NetNodeNum][2] = { 0 };  //消费节点与和它最近的消费节点的点数累计  0表示累计数,1表示标号

/*****************************************************************************/
unsigned short ConnectedNodePoint[Max_NetNodeNum][MaxConnect] = { 0 };      //用来保存与消费节点相关的点,一维下标是消费节点编号,从0开始,二维下标是相关的点
unsigned short ConnectedNodePointNum[Max_NetNodeNum] = { 0 };               //用来保存与消费节点相关的点的个数
unsigned short ConnectedExpensePoint[Max_NetNodeNum][MaxConnect] = { 0 };   //用来保存普通节点被哪些消费节点遍历了,一维是普通节点的编号,二维是消费节点的编号
unsigned short ConnectedExpensePointNum[Max_NetNodeNum] = { 0 };        //用来保存每个普通节点被遍历了几次
unsigned short NodeNum[Max_NetNodeNum][2] = { 0 };                          //记录普通节点相关的点数
unsigned short SortNode[Max_NetNodeNum] = { 0 };                        //得到普通节点排序的结果
/*******************************************************************************/


//*****************************************************************************
//计算Dijstala所需要的变量以及更新服务器的变量
unsigned short SearchedPoint[Max_NetNodeNum] = { 0 };   //定义已经找到的点,找到的话置为累计长度
unsigned short ConnectedMin_ID_Test[Max_NetNodeNum][Max_NetNodeNum] = { 0 };    //定义任意两点这间最小距离的ID链接,起始点,终止点,结果上一个的编号
unsigned short PaiXu[Max_NetNodeNum] = { 0 };
unsigned short ConnectedNum_Pre[Max_NetNodeNum][2] = { 0 };//高位表示第几个点,低位0表示被搜的编号,1表示搜到最小值的编号
unsigned short LastPoint[Max_NetNodeNum] = { 0 };           //一共找到的点数组
unsigned short SameLegthPoint[Max_NetNodeNum] = { 0 };  //当前层找到的最短路径数组
unsigned short RepeatBuf[Max_NetNodeNum] = { 0 };   //重复的Buf
//********************************************************************************


//********************************************************************************
//模拟退火算法参数
#define Mapkob_Line 27
#define AccuracyOut 1
#define ControlT    10000000
#define ControlPara 0.3
#define Q           1.5

float ControlT_Para = 0;

unsigned short Mapkob_L = 0;
unsigned short CurrentServerBuf[Max_NetNodeNum]={0};
unsigned short CurrentServerNum = 0;
unsigned long CurrentExpense = MaxMoney;
//********************************************************************************

#define NewPointNum 4

void deploy_server(char * topo[MAX_EDGE_NUM], int line_num, char * filename)
{
    time_t   long_time_start, long_time_end;
    time(&long_time_start);                             /*   Get   time   as   long   integer.   */
    long int TimeRunning = 0;
    // 需要输出的内容
    //char * topo_file2 = (char *)"17\n\n0 8 0 20\n21 8 0 20\n9 11 1 13\n21 22 2 20\n23 22 2 8\n1 3 3 11\n24 3 3 17\n27 3 3 26\n24 3 3 10\n18 17 4 11\n1 19 5 26\n1 16 6 15\n15 13 7 13\n4 5 8 18\n2 25 9 15\n0 7 10 10\n23 24 11 23";
    char topo_file[MAX_EDGE_NUM] = { 0 };
    unsigned short TopoNum = 0;
    GetGraph(topo, line_num);   //获取图表信息

    if (CurrentGragh.NetNodeNum <= MODE_LOW) IsUsingRoom = false;   //判断工作类型,点数少时使用距离计算
    else IsUsingRoom = true;                                            //点数多时用容量计算

    //判断孤立节点
    GetSinglePoint();       //寻找孤立节点
    LenthSum();             //确定服务器排序
    SelectedServerNum = 40; //从3个服务器开始搜
    UnsatifiedNum = CurrentGragh.ExpenseNum;
    NowServerNum = SelectedServerNum;
    PreServerNum = SelectedServerNum;
    GetExpenseMin();
    if (true)
    {
        NowServerNum = 0; PreServerNum = 0;
        for (int i = 0; i < CurrentGragh.ExpenseNum; i++)
        {
            NowSearchedPath[NowServerNum++] = ExpensePointID[i] - 1;
            PreSearchedPath[PreServerNum++] = ExpensePointID[i] - 1;
        }
        UpdateMaxie();                              //更新数据矩阵
        UpdateServerAll(NowServerNum);//更新服务器位置
        IsSatified = GetMaxFlow();  //计算并判断是否满足情况
        if (IsSatified == true) ChargeALL = GetCharge(topo_file, false);//得到当前方案满足的钱数
    }

    if (CurrentGragh.NetNodeNum <= MODE_LOW) { PreServerNum = NowServerNum; NowServerNum = NowServerNum; ChargeALL = MaxMoney; }//点数小时,全局搜索找最优
    else if (CurrentGragh.NetNodeNum <= MODE_Middle) { PreServerNum = NowServerNum; PreServerNum = NowServerNum; ChargeALL = MaxMoney; }    //中级
    else {PreServerNum = NowServerNum = NowServerNum;}                                          //高级

    SelectedServerNum = NowServerNum;   //服务器个数

    PreServerNum = SelectedServerNum;
    NowServerNum = SelectedServerNum;   //重新赋值
    PreChargeALL = ChargeALL;           //保存上次钱数


    static long TempCharge = PreChargeALL;
    static long TempDeleteNum = 0;
    static bool IsDelete = false;
    static float aaa;
    static float bbb;
    int Temp1 = 0;
    ControlT_Para = ControlT;

    //****************************************************************初中级方案  start*********************************************************************
    while (CurrentGragh.NetNodeNum <= MODE_Middle)
    {
        for (int i = PreServerNum - 1; i >= 0; i--)     //循环次数
        {
            time(&long_time_end);                             /*   Get   time   as   long   integer.   */
            TimeRunning = long_time_end - long_time_start;
            if (TimeRunning >= 89) break;
            UpdateMaxie();                              //更新数据矩阵
            DeleteNum = i; NowServerNum = 0;

            for (int DeleteCheck = 0; DeleteCheck < CantDeleteNum; DeleteCheck++)
            {
                if (DeleteNum == Cant_Delete[DeleteCheck])
                {
                    IsDelete = true;
                    break;
                }
            }

            if (IsDelete == true) { IsDelete = false; continue; }

            for (int j = 0; (j < PreServerNum); j++)
            {
                if ((j != DeleteNum))
                    NowSearchedPath[NowServerNum++] = PreSearchedPath[j];//更新路径
            }
            SelectedServerNum = NowServerNum;   //当前点数
            UpdateServerAll(NowServerNum);//更新服务器位置
            IsSatified = GetMaxFlow();  //计算并判断是否满足情况
            if (IsSatified == true)
            {
                ChargeALL = GetCharge(topo_file, false);                //得到当前方案的钱数
                if (TempCharge > ChargeALL) { TempCharge = ChargeALL; TempDeleteNum = DeleteNum; }//if (CurrentGragh.NetNodeNum > MODE_Middle) break; }
            }
            else
            {
                bool IsCanInput = true;
                for (int l = 0; l < CantDeleteNum; l++)
                {
                    if (PreSearchedPath[DeleteNum] == Cant_Delete[l]) IsCanInput = false;
                }
                if (IsCanInput == true) Cant_Delete[CantDeleteNum++] = PreSearchedPath[DeleteNum];
                continue;
            }
        }
        if (TempCharge < PreChargeALL)
        {
            NowServerNum = PreServerNum; PreServerNum = 0;
            PreChargeALL = TempCharge;
            for (int j = 0; (j < NowServerNum); j++)
            {
                if ((j != TempDeleteNum))
                    PreSearchedPath[PreServerNum++] = PreSearchedPath[j];//更新路径
            }
        }
        else if (TempCharge == PreChargeALL) break;
    }

    //加点删除策略***************************************************************************************
    if (CurrentGragh.NetNodeNum <= MODE_Middle)
    {
        CurrentServerNum = 0;
        for (int i = 0; i < PreServerNum; i++) CurrentServerBuf[CurrentServerNum++] = PreSearchedPath[i];
        TempCharge = MaxMoney; CurrentExpense = PreChargeALL; PreChargeALL = MaxMoney;
    }
    while (CurrentGragh.NetNodeNum <= MODE_Middle)
    {

        time(&long_time_end);                             /*   Get   time   as   long   integer.   */
        TimeRunning = long_time_end - long_time_start;
        if (TimeRunning >= 89) break;

        PreServerNum = 0;
        for (int i = 0; i < CurrentServerNum; i++) PreSearchedPath[PreServerNum++] = CurrentServerBuf[i];

        TempCharge = MaxMoney; PreChargeALL = MaxMoney;
        unsigned short AddPointNum = 0;
        unsigned short PointTemp = 0;
        for (int i = 0; i < NewPointNum;)   //添加随机点
        {
            PointTemp = rand() % (CurrentGragh.NetNodeNum - 1);     //产生0-PreServerNum-1的随机数
            bool IsInBuf = false;
            for (int j = 0; j < PreServerNum; j++)
            {
                if (PointTemp == PreSearchedPath[j])
                {
                    IsInBuf = true;
                    break;
                }
            }
            if (IsInBuf == true) continue;
            PreSearchedPath[PreServerNum++] = PointTemp;
            i++;
        }
        memset(Cant_Delete, 0, Max_NetNodeNum*sizeof(unsigned short));
        while (CurrentGragh.NetNodeNum <= MODE_Middle)
        {
            for (int i = PreServerNum - 1; i >= 0; i--)     //循环次数
            {
                time(&long_time_end);                             /*   Get   time   as   long   integer.   */
                TimeRunning = long_time_end - long_time_start;
                if (TimeRunning >= 89) break;
                UpdateMaxie();                              //更新数据矩阵
                DeleteNum = i; NowServerNum = 0;

                for (int DeleteCheck = 0; DeleteCheck < CantDeleteNum; DeleteCheck++)
                {
                    if (DeleteNum == Cant_Delete[DeleteCheck])
                    {
                        IsDelete = true;
                        break;
                    }
                }

                if (IsDelete == true) { IsDelete = false; continue; }

                for (int j = 0; (j < PreServerNum); j++)
                {
                    if ((j != DeleteNum))
                        NowSearchedPath[NowServerNum++] = PreSearchedPath[j];//更新路径
                }
                SelectedServerNum = NowServerNum;   //当前点数
                UpdateServerAll(NowServerNum);//更新服务器位置
                IsSatified = GetMaxFlow();  //计算并判断是否满足情况
                if (IsSatified == true)
                {
                    ChargeALL = GetCharge(topo_file, false);                //得到当前方案的钱数
                    if (TempCharge > ChargeALL) { TempCharge = ChargeALL; TempDeleteNum = DeleteNum; }//if (CurrentGragh.NetNodeNum > MODE_Middle) break; }
                }
                else
                {
                    bool IsCanInput = true;
                    for (int l = 0; l < CantDeleteNum; l++)
                    {
                        if (PreSearchedPath[DeleteNum] == Cant_Delete[l]) IsCanInput = false;
                    }
                    if (IsCanInput == true) Cant_Delete[CantDeleteNum++] = PreSearchedPath[DeleteNum];
                    continue;
                }
            }
            if (TempCharge < PreChargeALL)
            {
                NowServerNum = PreServerNum; PreServerNum = 0;
                PreChargeALL = TempCharge;
                for (int j = 0; (j < NowServerNum); j++)
                {
                    if ((j != TempDeleteNum))
                        PreSearchedPath[PreServerNum++] = PreSearchedPath[j];//更新路径
                }
            }
            else if (TempCharge == PreChargeALL) break;
        }

        if (CurrentExpense > PreChargeALL)
        {
            CurrentServerNum = 0; CurrentExpense = PreChargeALL;
            for (int i = 0; i < PreServerNum; i++)
                CurrentServerBuf[CurrentServerNum++] = PreSearchedPath[i];
        }
        PRINT("当前费用:%d", CurrentExpense);
        PRINT("计算费用:%d", PreChargeALL);
        PRINT("   当前时间:%d", TimeRunning);
        PRINT("   当前服务器数:%d\n", PreServerNum);
    }
    if (CurrentGragh.NetNodeNum < MODE_Middle)
    {
        PreServerNum = 0; PreChargeALL = CurrentExpense;
        for (int i = 0; i < CurrentServerNum; i++) PreSearchedPath[PreServerNum++] = CurrentServerBuf[i];
    }

    //****************************************************************高级方案  start*********************************************************************
    while (CurrentGragh.NetNodeNum > MODE_Middle)
    {
        for (int MapkobLine = 0; MapkobLine < Mapkob_Line; MapkobLine++)    //Mapkob 链长
        {
            time(&long_time_end);                             /*   Get   time   as   long   integer.   */
            TimeRunning = long_time_end - long_time_start;
            if (TimeRunning >= 90) break;
            UpdateMaxie();                              //更新数据矩阵
            DeleteNum = rand() % (PreServerNum - 1);        //产生0-PreServerNum-1的随机数
            NowServerNum = 0;

            for (int DeleteCheck = 0; DeleteCheck < CantDeleteNum; DeleteCheck++)
            {
                if (DeleteNum == Cant_Delete[DeleteCheck])
                {
                    IsDelete = true;
                    break;
                }
            }

            if (IsDelete == true) { IsDelete = false; continue; }

            for (int j = 0; (j < PreServerNum); j++)
            {
                if ((j != DeleteNum))
                    NowSearchedPath[NowServerNum++] = PreSearchedPath[j];//更新路径
            }
            SelectedServerNum = NowServerNum;   //当前点数
            UpdateServerAll(NowServerNum);//更新服务器位置
            IsSatified = GetMaxFlow();  //计算并判断是否满足情况
            if (IsSatified == true)
            {
                ChargeALL = GetCharge(topo_file, false);                //得到当前方案的钱数
                if (TempCharge > ChargeALL) { TempCharge = ChargeALL; TempDeleteNum = DeleteNum; }//if (CurrentGragh.NetNodeNum > MODE_Middle) break; }
            }
            else
            {
                bool IsCanInput = true;
                for (int l = 0; l < CantDeleteNum; l++)
                {
                    if (PreSearchedPath[DeleteNum] == Cant_Delete[l]) IsCanInput = false;
                }
                if (IsCanInput == true) Cant_Delete[CantDeleteNum++] = PreSearchedPath[DeleteNum];
                continue;
            }
        }
        if (TempCharge < PreChargeALL)
        {
            NowServerNum = PreServerNum; PreServerNum = 0;
            PreChargeALL = TempCharge;
            for (int j = 0; (j < NowServerNum); j++)
            {
                if ((j != TempDeleteNum))
                    PreSearchedPath[PreServerNum++] = PreSearchedPath[j];//更新路径
            }
        }
        else if (TempCharge == PreChargeALL) break;
        PRINT("当前费用:%d", PreChargeALL);
            PRINT("   当前时间:%d", TimeRunning);
            PRINT("   当前服务器数:%d\n", NowServerNum);
    }


    SelectedServerNum = NowServerNum;
    NowServerNum = PreServerNum; NetPathALL = 0;
    if (CurrentGragh.NetNodeNum <= MODE_Middle) //初级和中级,服务器交换
    {
        for (int i = 0; i < NowServerNum; i++) NowSearchedPath[i] = PreSearchedPath[i];
        for (int i = 0; i < CurrentGragh.ExpenseNum; i++)   //判断某个消费节点是不是服务器
        {
            bool IsServer = false;
            unsigned short SearchedServerNum = 0;
            for (int j = 0; j < NowServerNum; j++)  //判断该消费节点是不是服务器,是第几个服务器
            {
                if (ExpensePointID[i] - 1 == NowSearchedPath[j])
                {
                    IsServer = true;
                    SearchedServerNum = j;
                    break;
                }
            }
            if (IsServer == false) continue;//如果该消费节点不是服务器,直接继续搜消费节点上的服务器
            //找到与该消费节点最近的消费节点,判断是不是服务器
            unsigned short ClosedLength = MaxERROR,CloseNum=0,Temp=0;
            for (int j = 0; j < CurrentGragh.ExpenseNum; j++)   //找到最短路径的消费节点
            {
                if (j != i)
                {
                    Temp = ConnectedLength_Min_Temp[ExpensePointID[i] - 1][ExpensePointID[j] - 1];//当前距离
                    if (Temp < ClosedLength)
                    {
                        ClosedLength = Temp;
                        CloseNum = j;
                    }
                }
            }
            IsServer = false;
            for (int j = 0; j < NowServerNum; j++)  //判断该消费节点是不是服务器,是第几个服务器
            {
                if (ExpensePointID[CloseNum] - 1 == NowSearchedPath[j])
                {
                    IsServer = true;
                    SearchedServerNum = j;
                    break;
                }
            }
            if (IsServer == false)  //如果该节点不是服务器,把该节点置为服务器,交换
            {
                NowSearchedPath[SearchedServerNum] = ExpensePointID[CloseNum] - 1;
                UpdateMaxie();                              //更新数据矩阵
                UpdateServerAll(NowServerNum);              //更新服务器位置
                IsSatified = GetMaxFlow();                  //计算并判断是否满足情况
                ChargeALL = GetCharge(topo_file, false);        //得到当前方案的钱数
                if ((IsSatified==true)&&(ChargeALL < PreChargeALL)) //当前钱数降了
                {
                    continue;
                }
                else
                {
                    NowSearchedPath[SearchedServerNum] = ExpensePointID[i] - 1;
                }
            }
        }
    }
    else
    {
        //for (int i = 0; i < CurrentServerNum; i++) NowSearchedPath[i] = CurrentServerBuf[i];
        NowServerNum = PreServerNum;
        for (int i = 0; i < NowServerNum; i++) NowSearchedPath[i] = PreSearchedPath[i];
    }


    //当前算得的点数大于消费节点数,或者当前消费大于直接放在服务器的消费数,直接把服务器放在消费节点位置上
    if ((NowServerNum >= CurrentGragh.ExpenseNum) || (PreChargeALL>CurrentGragh.ExpenseNum*CurrentGragh.ServerCost))
    {
        PreServerNum = CurrentGragh.ExpenseNum;
        NowServerNum = CurrentGragh.ExpenseNum;
        for (int i = 0; i < CurrentGragh.ExpenseNum; i++)
        {
            NowSearchedPath[i] = ExpensePointID[i] - 1; //服务器位置
        }
    }

    UpdateMaxie();                              //更新数据矩阵
    UpdateServerAll(NowServerNum);              //更新服务器位置
    IsSatified = GetMaxFlow();                  //计算并判断是否满足情况
    ChargeALL = GetCharge(topo_file, true);     //得到当前方案的钱数
    if (IsSatified == true)  PRINT("\n答案正确!\n");
    else PRINT("\n答案错误!\n");
    /*
    char aaa[2];
    itoa(10, aaa, 10);*/
    unsigned short PreID = 0;
    unsigned short NowID = 0;
    int kk = 0;
    bool IsContinue = false;


    //*************************************************写入文件系统start****************************************************//
    sprintf(&topo_file[0], "%ld", NetPathALL); while ((topo_file[TopoNum] >= 0x30) && (topo_file[TopoNum] <= 0x39)) TopoNum++;
    sprintf(&topo_file[TopoNum++], "%s", "\n"); sprintf(&topo_file[TopoNum++], "%s", "\n"); 
    for (int x = 0; x < NowServerNum; x++)
    {
        for (int i = 0; i < CurrentGragh.ExpenseNum; i++)   //消费节点
        {
            for (int j = 0; j < Path_Num[i][0]; j++)        //消费节点边数
            {
                PreID = ExpensePointID[i] - 1;//消费节点ID
                kk = 0;
                for (kk = 0; Path[i][j][kk] != -1; kk++) {} kk--;
                if (kk == -1) kk = 0;
                if ((Path[i][j][kk] == NowSearchedPath[x]))
                    IsContinue = true;
                if (IsContinue == true)
                {
                    while (kk >= 0)
                    {
                        sprintf(&topo_file[TopoNum], "%ld", Path[i][j][kk]); while ((topo_file[TopoNum] >= 0x30) && (topo_file[TopoNum] <= 0x39)) TopoNum++;
                        sprintf(&topo_file[TopoNum], "%s", " "); TopoNum++;
                        kk--;
                    }
                    sprintf(&topo_file[TopoNum], "%ld", ExpensePointID[i] - 1); while ((topo_file[TopoNum] >= 0x30) && (topo_file[TopoNum] <= 0x39)) TopoNum++;
                    sprintf(&topo_file[TopoNum], "%s", " "); TopoNum++;
                    sprintf(&topo_file[TopoNum], "%ld", i); while ((topo_file[TopoNum] >= 0x30) && (topo_file[TopoNum] <= 0x39)) TopoNum++;
                    sprintf(&topo_file[TopoNum], "%s", " "); TopoNum++;
                    sprintf(&topo_file[TopoNum], "%ld", PathExpense[i][j]); while ((topo_file[TopoNum] >= 0x30) && (topo_file[TopoNum] <= 0x39)) TopoNum++;
                    if ((x != NowServerNum - 1) || (i != CurrentGragh.ExpenseNum - 1) || (j != Path_Num[i][0] - 1))
                    {
                        sprintf(&topo_file[TopoNum], "%s", "\n"); TopoNum += 1;
                    }
                }
                else if (ExpensePointID[i] - 1 == NowSearchedPath[x])
                {
                    sprintf(&topo_file[TopoNum], "%ld", ExpensePointID[i] - 1); while ((topo_file[TopoNum] >= 0x30) && (topo_file[TopoNum] <= 0x39)) TopoNum++;
                    sprintf(&topo_file[TopoNum], "%s", " "); TopoNum++;
                    sprintf(&topo_file[TopoNum], "%ld", i); while ((topo_file[TopoNum] >= 0x30) && (topo_file[TopoNum] <= 0x39)) TopoNum++;
                    sprintf(&topo_file[TopoNum], "%s", " "); TopoNum++;
                    sprintf(&topo_file[TopoNum], "%ld", PathExpense[i][j]); while ((topo_file[TopoNum] >= 0x30) && (topo_file[TopoNum] <= 0x39)) TopoNum++;
                    if ((x != NowServerNum - 1) || (i != CurrentGragh.ExpenseNum - 1) || (j != Path_Num[i][0] - 1))
                    {
                        sprintf(&topo_file[TopoNum], "%s", "\n"); TopoNum += 1;
                    }
                }
                IsContinue = false;
            }
        }
    }
    if (topo_file[TopoNum - 1] == 0x0a) topo_file[TopoNum - 1] = 0;
    //*************************************************写入文件系统end****************************************************//
    // 直接调用输出文件的方法输出到指定文件中(ps请注意格式的正确性,如果有解,第一行只有一个数据;第二行为空;第三行开始才是具体的数据,数据之间用一个空格分隔开)
    write_result(topo_file, filename);
}


/*********************************************
函数名:GetNum(char * graph[MAX_EDGE_NUM],unsigned short X_Line,bool Isclear)
函数功能:得到数据
函数变量:graph[MAX_EDGE_NUM],unsigned short X_Line,bool Isclear
函数返回值:unsined int
**********************************************/
unsigned short GetNum(char * graph[MAX_EDGE_NUM], unsigned short X_Line)
{
    unsigned short RetureValue = 0;
    static unsigned char Y_Line = 0;
    static unsigned short X_Line_Last = 0;
    if (X_Line_Last != X_Line) Y_Line = 0;
    while ((*(graph[X_Line] + Y_Line) < 0x30) || (*(graph[X_Line] + Y_Line)>0x39)) Y_Line++;
    while ((*(graph[X_Line] + Y_Line) >= 0x30) && (*(graph[X_Line] + Y_Line) <= 0x39))
    {
        RetureValue = RetureValue * 10 + *(graph[X_Line] + Y_Line) - 0x30;
        Y_Line++;
    }
    X_Line_Last = X_Line;
    return RetureValue;
}



/*********************************************
函数名:GetGraph(char * graph[MAX_EDGE_NUM])
函数功能:导入文件,生成图数组
函数变量:graph[MAX_EDGE_NUM]
函数返回值:null
**********************************************/

void GetGraph(char * graph_input[MAX_EDGE_NUM], int edge_num_input)
{
    int i = 0, j = 0;
    unsigned short Net_X, Net_Y;
    static bool IsFirstInput = false;
    if (IsFirstInput == true) return;
    while ((graph_input[i][0] < 0x30) || (graph_input[i][0]>0x39)) i++; //获取第一行点数、链路数、消费节点数
    CurrentGragh.NetNodeNum = GetNum(graph_input, i);
    CurrentGragh.NetLinkNum = GetNum(graph_input, i);
    CurrentGragh.ExpenseNum = GetNum(graph_input, i); i++;

    while ((graph_input[i][0] < 0x30) || (graph_input[i][0]>0x39)) i++; //获取第三行,服务器价格
    CurrentGragh.ServerCost = GetNum(graph_input, i); i++;
    while ((graph_input[i][0] < 0x30) || (graph_input[i][0]>0x39)) i++;
    while ((graph_input[i][0] >= 0x30) && (graph_input[i][0] <= 0x39))  //获取普通网络节点的链接关系,生成链接矩阵
    {
        Net_X = GetNum(graph_input, i);
        Net_Y = GetNum(graph_input, i);
        //CurrentGragh.Net_Graph[Net_X][Net_Y].NetMode = Normal_Net;
        //CurrentGragh.Net_Graph[Net_Y][Net_X].NetMode = Normal_Net;
        CurrentGragh.Net_Graph[Net_X][Net_Y].BandWidth_Max =
            CurrentGragh.Net_Graph[Net_Y][Net_X].BandWidth_Max = GetNum(graph_input, i);
        CurrentGragh.Net_Graph[Net_X][Net_Y].SingleCost =
            CurrentGragh.Net_Graph[Net_Y][Net_X].SingleCost = GetNum(graph_input, i);
        BandWidth[BandWidthNum][0] = Net_X;
        BandWidth[BandWidthNum][1] = Net_Y;
        BandWidth[BandWidthNum][2] = CurrentGragh.Net_Graph[Net_X][Net_Y].BandWidth_Max;
        BandWidth[BandWidthNum++][3] = CurrentGragh.Net_Graph[Net_X][Net_Y].SingleCost;

        if (IsFirstInput == false)
        {
            ConnectRelation[Net_X][ConnectNum[Net_X]++] = Net_Y;
            ConnectRelation[Net_Y][ConnectNum[Net_Y]++] = Net_X;
        }
        i++;
    }
    while ((graph_input[i][0] < 0x30) || (graph_input[i][0]>0x39)) i++;
    short ExpenseLine = 0;
    while ((ExpenseLine<CurrentGragh.ExpenseNum)&&(graph_input[i][0] >= 0x30) && (graph_input[i][0] <= 0x39))   //获得消费节点链接关系,生成消费矩阵
    {
        Net_X = GetNum(graph_input, i);
        Net_Y = GetNum(graph_input, i);
        CurrentGragh.Net_Graph[Net_X + CurrentGragh.NetNodeNum][Net_Y].ConnectID = Net_Y;
        CurrentGragh.Net_Graph[Net_Y][Net_X + CurrentGragh.NetNodeNum].ConnectID = Net_Y;
        //CurrentGragh.Net_Graph[Net_X + CurrentGragh.NetNodeNum][Net_Y].NetMode = Expense_Net;
        //CurrentGragh.Net_Graph[Net_Y][Net_X + CurrentGragh.NetNodeNum].NetMode = Expense_Net;
        CurrentGragh.Net_Graph[Net_Y][Net_X + CurrentGragh.NetNodeNum].BandWidth_Need =
            CurrentGragh.Net_Graph[Net_X + CurrentGragh.NetNodeNum][Net_Y].BandWidth_Need = GetNum(graph_input, i);
        ExpensePointID[Net_X] = Net_Y + 1;

        ExpenseBandNeed[Net_X] = CurrentGragh.Net_Graph[Net_X + CurrentGragh.NetNodeNum][Net_Y].BandWidth_Need;

        i++; ExpenseLine++;
    }
    IsFirstInput = true;
}

/*********************************************
函数名:unsigned short Dijkstra_Graph(struct ParaNetStruct GraphNet[Max_NetNodeNum][Max_NetNodeNum])
函数功能:导入图像矩阵,计算初始点和结束点最短距离
函数变量:Start_Point:初始点   End_Point:结束点
函数返回值:节点距离
**********************************************/
//unsigned short Dijkstra_Graph(unsigned short Start_Point, unsigned short End_Point)
//{
//  unsigned short Connect_Min = MaxERROR;          //定义与某一点相连的路径最小值
//  unsigned short Current_Aspect = 0;          //当前层,或者与当前寻找的次数
//  unsigned short SearchedPoint[1000] = { 0 };   //定义已经找到的点,找到的话置为累计长度
//  unsigned short Current_Point = 0;               //当前点
//  unsigned short SameLegthNum = 0;                //当前层找到的最小路径数量
//  unsigned short SameLegthPoint[1000] = { 0 };    //当前层找到的最短路径数组
//  unsigned short i = 0;
//  unsigned short Sum_Legtgh = 0;              //累计长度
//  int LastNum = 1;                            //一共找到的点数
//  unsigned short LastPoint[1000] = { 0 };         //一共找到的点数组
//  LastPoint[0] = Start_Point;                 //第一次赋值
//  unsigned short ConnectedNum_Pre[1000][2] = { 0 };//高位表示第几个点,低位0表示被搜的编号,1表示搜到最小值的编号
//
//  for (Current_Aspect = 0; Current_Aspect < CurrentGragh.NetNodeNum; Current_Aspect++)    //表示是第几次寻找
//  {
//      for (int n = 0; n < LastNum; n++)
//      {
//          Current_Point = LastPoint[n];
//          if ((SearchedPoint[Current_Point] != 0) || (Current_Point == Start_Point))      //此点已经存在,即已经被搜寻过
//          {
//              for (int k = 0; k < ConnectNum[Current_Point]; k++)
//              {
//                  i = ConnectRelation[Current_Point][k];
//                  if ((i != Start_Point) && (Connect_Min >= SearchedPoint[Current_Point] + CurrentGragh.Net_Graph[Current_Point][i].SingleCost) && (CurrentGragh.Net_Graph[Current_Point][i].BandWidth_Max != 0) && (SearchedPoint[i] == 0))  //找到这一层的最小值
//                  {
//                      if (Connect_Min>SearchedPoint[Current_Point] + CurrentGragh.Net_Graph[Current_Point][i].SingleCost) //最小值更新
//                      {
//                          SameLegthNum = 0;
//                      }
//                      Connect_Min = SearchedPoint[Current_Point] + CurrentGragh.Net_Graph[Current_Point][i].SingleCost;   //当前最小值
//                      ConnectedNum_Pre[SameLegthNum][0] = Current_Point;                  //记录被搜的编号
//                      ConnectedNum_Pre[SameLegthNum][1] = i;                              //记录搜到最小值的编号
//                      SameLegthPoint[SameLegthNum++] = i;                                 //记录最小值个数以及座标
//                  }
//              }
//          }
//      }
//
//      if (SameLegthNum == 0) break;                                                       //没有找到点,退出搜索
//      for (i = 0; i < SameLegthNum; i++)
//      {
//          SearchedPoint[SameLegthPoint[i]] = Connect_Min;                                 //放入最短长度
//          ConnectedLength_Min[Start_Point][SameLegthPoint[i]] = Connect_Min;//最小距离放入链接数组
//          ConnectedMin_ID[Start_Point][ConnectedNum_Pre[i][1]] = ConnectedNum_Pre[i][0];
//
//          if (IsInited == false)
//          {
//              ConnectedLength_Min_Temp[Start_Point][SameLegthPoint[i]] = Connect_Min;
//              ConnectedLength_Min_Temp[SameLegthPoint[i]][Start_Point] = Connect_Min;             //最小距离放入链接数组
//              ConnectedMin_ID_Temp[Start_Point][ConnectedNum_Pre[i][1]] = ConnectedNum_Pre[i][0];
//          }
//
//          LastPoint[LastNum++] = SameLegthPoint[i];                                       //记录已经被找过的点数
//          if (SameLegthPoint[i] == End_Point)
//              return Connect_Min;
//      }
//      SameLegthNum = 0; Connect_Min = MaxERROR;                                               //累计清零
//  }
//}

/*********************************************
函数名:void Dijkstra_Graph_2(unsigned short Start_Point)
函数功能:导入图像矩阵,计算其他所有节点到初始节点最短距离
函数变量:Start_Point:初始点
函数返回值:null
**********************************************/

void Dijkstra_Graph_2(unsigned short Start_Point)
{
    unsigned short Connect_Min = MaxERROR;          //定义与某一点相连的路径最小值
    unsigned short Current_Aspect = 0;          //当前层,或者与当前寻找的次数
    unsigned short Current_Point = 0;               //当前点
    unsigned short SameLegthNum = 0;                //当前层找到的最小路径数量
    unsigned short i = 0;
    unsigned short Sum_Legtgh = 0;              //累计长度
    int LastNum = 1;                            //一共找到的点数

    memset(SearchedPoint, 0, Max_NetNodeNum*sizeof(unsigned short));

    LastPoint[0] = Start_Point;                 //第一次赋值

    for (Current_Aspect = 0; Current_Aspect < CurrentGragh.NetNodeNum; Current_Aspect++)    //表示是第几次寻找
    {
        for (int n = 0; n < LastNum; n++)
        {
            Current_Point = LastPoint[n];
            if ((SearchedPoint[Current_Point] != 0) || (Current_Point == Start_Point))      //此点已经存在,即已经被搜寻过
            {
                for (int k = 0; k < ConnectNum[Current_Point]; k++)
                {
                    i = ConnectRelation[Current_Point][k];
                    if ((i != Start_Point) && (Connect_Min >= SearchedPoint[Current_Point] + CurrentGragh.Net_Graph[Current_Point][i].SingleCost) && (CurrentGragh.Net_Graph[Current_Point][i].BandWidth_Max != 0) && (SearchedPoint[i] == 0))  //找到这一层的最小值
                    {
                        if (Connect_Min>SearchedPoint[Current_Point] + CurrentGragh.Net_Graph[Current_Point][i].SingleCost) //最小值更新
                        {
                            SameLegthNum = 0;
                        }
                        Connect_Min = SearchedPoint[Current_Point] + CurrentGragh.Net_Graph[Current_Point][i].SingleCost;   //当前最小值
                        ConnectedNum_Pre[SameLegthNum][0] = Current_Point;                  //记录被搜的编号
                        ConnectedNum_Pre[SameLegthNum][1] = i;                              //记录搜到最小值的编号
                        SameLegthPoint[SameLegthNum++] = i;                                 //记录最小值个数以及座标
                    }
                }
            }
        }

        if (SameLegthNum == 0) break;                                                       //没有找到点,退出搜索
        for (i = 0; i < SameLegthNum; i++)
        {
            SearchedPoint[SameLegthPoint[i]] = Connect_Min;                                 //放入最短长度
            ConnectedLength_Min[Start_Point][SameLegthPoint[i]] = Connect_Min;
            ConnectedMin_ID[Start_Point][ConnectedNum_Pre[i][1]] = ConnectedNum_Pre[i][0];

            if (IsInited == false)
            {
                ConnectedLength_Min_Temp[Start_Point][SameLegthPoint[i]] = Connect_Min;
                ConnectedLength_Min_Temp[SameLegthPoint[i]][Start_Point] = Connect_Min;             //最小距离放入链接数组
                ConnectedMin_ID_Temp[Start_Point][ConnectedNum_Pre[i][1]] = ConnectedNum_Pre[i][0];
            }

            LastPoint[LastNum++] = SameLegthPoint[i];                                       //记录已经被找过的点数
        }
        SameLegthNum = 0; Connect_Min = MaxERROR;                                               //累计清零
    }
}




/*********************************************
函数名:unsigned short MinPoint( unsigned short servernum, unsigned short expensepoint)
函数功能:选出与该消费节点相连的最短的服务器
函数变量:servernum是服务器的个数,expensepoint是消费节点的ID
函数返回值:服务器标号
**********************************************/
unsigned short MinPoint(unsigned short servernum, unsigned short expensepoint)//servernum是服务器的个数,expensepoint是消费节点的ID,选出与该消费节点相连的最短的服务器
{
    unsigned short i = 0;
    unsigned short MinServerPoint = 0;
    unsigned short MiniPath = MaxERROR;
    for (i = 0; i < servernum; i++)
    {
        if (ConnectedLength_Min[expensepoint][NowSearchedPath[i]] < MiniPath)
        {
            MiniPath = ConnectedLength_Min[expensepoint][NowSearchedPath[i]];
            MinServerPoint = NowSearchedPath[i];
        }
    }
    return MinServerPoint;
}


/*********************************************
函数名:void LenthSum()
函数功能:计算所有消费节点到任意节点最短距离总和
函数变量:null
函数返回值:null
**********************************************/
void LenthSum()
{
#ifdef UsingDistance
    if (IsUsingRoom == false)
    {
        static unsigned short Sum = 0;
        for (int i = 0; i < CurrentGragh.NetNodeNum; i++)
        {
            Dijkstra_Graph_2(i);//找第i个节点所有连接距离
            for (int j = 0; j < CurrentGragh.ExpenseNum; j++)
            {
                Sum += ConnectedLength_Min[i][ExpensePointID[j] - 1];
            }
            //PRINT("到%d结点距离:%d\n", i, Sum);
            SumLength[i][0] = Sum;
            SumLength[i][1] = i;
            for (int k = 0; k < SinglePointNum; k++)
            {
                if (i == ExpensePointID[SinglePoint[k]] - 1)
                {
                    SumLength[i][0] = 0;
                    SumLength[i][1] = i;
                    break;
                }
            }
            Sum = 0;
        }
        MaoPao(SumLength, CurrentGragh.NetNodeNum);
        for (int i = 0; i < CurrentGragh.NetNodeNum; i++)
        {
            PreSearchedPath[PreServerNum++] = SumLength[i][1];//保存第一次计算的路径排序
            NowSearchedPath[NowServerNum++] = SumLength[i][1];
            //PRINT("到%d结点距离:%d\n", SumLength[i][1], SumLength[i][0]);
        }
        IsInited = true;
    }
#endif // UsingDistance
#ifdef UsingRoom
    if (IsUsingRoom == true)
    {
        unsigned short Sum = 0;
        unsigned short LineNum = 0;
        for (int i = 0; i < CurrentGragh.NetNodeNum; i++)
        {
            Dijkstra_Graph_2(i);//找第i个节点所有连接距离
            for (int j = 0; j < CurrentGragh.NetNodeNum; j++)
            {
                Sum += CurrentGragh.Net_Graph[i][j].BandWidth_Max;
                if (CurrentGragh.Net_Graph[i][j].BandWidth_Max != 0) LineNum++;
            }
            SumLength[i][0] = Sum*LineNum;
            SumLength[i][1] = i;
            Sum = 0;
            LineNum = 0;
            for (int k = 0; k < SinglePointNum; k++)
            {
                if (i == ExpensePointID[SinglePoint[k]] - 1)
                {
                    SumLength[i][0] = MaxERROR; //最大
                    SumLength[i][1] = i;
                    break;
                }
            }
        }
        MaoPao(SumLength, CurrentGragh.NetNodeNum);
        for (int i = 0; i < CurrentGragh.NetNodeNum; i++)
        {
            PreSearchedPath[PreServerNum++] = SumLength[i][1];//保存第一次计算的路径排序
            NowSearchedPath[NowServerNum++] = SumLength[i][1];
            //PRINT("到%d结点距离:%d\n", SumLength[i][1], SumLength[i][0]);
        }
        IsInited = true;
    }
#endif // UsingRoom
}

/*********************************************
函数名:void MaoPao(unsigned short Point[Max_NetNodeNum][2], unsigned short PointNum)
函数功能:冒泡法排序
函数变量:Point[Max_NetNodeNum][2]:服务器数组,PointNum:普通节点点数
函数返回值:null
**********************************************/
void MaoPao(unsigned short Point[Max_NetNodeNum][2], unsigned short PointNum)
{
    int i = 0, j = 0;
    unsigned short Temp = 0;//存放最小值
    unsigned short idTemp = 0;
    for (i = 0; i < PointNum; i++)
    {
        for (j = i + 1; j < PointNum; j++)
        {
#ifdef UsingDistance
            if ((Point[i][0]>Point[j][0]) && (IsUsingRoom == false))
            {

                Temp = Point[i][0];
                Point[i][0] = Point[j][0];
                Point[j][0] = Temp;
                idTemp = Point[i][1];
                Point[i][1] = Point[j][1];
                Point[j][1] = idTemp;
            }
#endif // UsingDistance

#ifdef UsingRoom
            if ((Point[i][0]<Point[j][0]) && (IsUsingRoom == true))
            {

                Temp = Point[i][0];
                Point[i][0] = Point[j][0];
                Point[j][0] = Temp;
                idTemp = Point[i][1];
                Point[i][1] = Point[j][1];
                Point[j][1] = idTemp;
            }
#endif // UsingDistance
        }
    }
}


/*********************************************
函数名:unsigned short GetMinFlow(unsigned short StartPoint, unsigned short EndPoint)
函数功能:计算最短距离里面的最小流量
函数变量:Start_Point:初始点服务器,EndPoint终止点消费节点 0-N
函数返回值:最短距离带宽
**********************************************/
unsigned short GetMinFlow(unsigned short StartPoint, unsigned short expenseID)
{
    unsigned short MinFlow = MaxERROR;          //表示最小流量
    unsigned short PreID = ExpensePointID[expenseID] - 1;           //链接的前一个点   
    unsigned short NowID = ExpensePointID[expenseID] - 1;           //当前点
    Path_Num[expenseID][1] = 0;//路径清零
    while (NowID != StartPoint)             //搜寻到初始点为止
    {
        PreID = ConnectedMin_ID[StartPoint][NowID];
        if (MinFlow > CurrentGragh.Net_Graph[PreID][NowID].BandWidth_Max)//当前最小带宽
            MinFlow = CurrentGragh.Net_Graph[PreID][NowID].BandWidth_Max;
        Path[expenseID][Path_Num[expenseID][0]][Path_Num[expenseID][1]++] = PreID;  //记录路径
        NowID = PreID;                                                    //循环赋值
    }
    Path[expenseID][Path_Num[expenseID][0]][Path_Num[expenseID][1]++] = -1;//路径结束标志
    return MinFlow;
}

/*********************************************
函数名:unsigned short UpdateGraph(unsigned short MinFlow, unsigned short serverID, unsigned short expenseID)
函数功能:更新图表并计算
函数变量:MinFlow:路径上最小带宽,serverID: 服务器地址   expenseID:消费节点编号  0-N
函数返回值:最短距离带宽
**********************************************/
unsigned short UpdateGraph(unsigned short MinFlow, unsigned short serverID, unsigned short expenseID)
{
    unsigned short needBandWidth = 0;
    unsigned short sendBandWidth = 0;
    unsigned short PreID = ExpensePointID[expenseID] - 1;           //链接的前一个点   
    unsigned short NowID = ExpensePointID[expenseID] - 1;           //当前点
    bool IsNeedRefresh = false;

    needBandWidth = CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + expenseID][ExpensePointID[expenseID] - 1].BandWidth_Need - CurrentGragh.Net_Graph[expenseID][ExpensePointID[expenseID] - 1].SatisfiedBandWidth;
    if (needBandWidth > MinFlow)//比较最大传输带宽和需要带宽大小
        sendBandWidth = MinFlow;
    else
        sendBandWidth = needBandWidth;
    PathExpense[expenseID][Path_Num[expenseID][0]] = sendBandWidth;//记录该路径上的带宽

    if (sendBandWidth!=0) Path_Num[expenseID][0]++;//路径条数++
    else return sendBandWidth;

    while (NowID != serverID)               //搜寻到初始点为止
    {
        PreID = ConnectedMin_ID[serverID][NowID];
        CurrentGragh.Net_Graph[PreID][NowID].BandWidth_Max = CurrentGragh.Net_Graph[PreID][NowID].BandWidth_Max - sendBandWidth;//更新网络节点剩余带宽
        if (CurrentGragh.Net_Graph[PreID][NowID].BandWidth_Max == 0)
        {
            IsNeedRefresh = true;
        }
        NowID = PreID;                                                    //循环赋值
    }
    if (IsNeedRefresh == true) UpdateServerAll(NowServerNum);// 更新服务器
    CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + expenseID][ExpensePointID[expenseID] - 1].BandWidth_Need -= sendBandWidth;//更新消费节点需要带宽
    return sendBandWidth;
}

/*********************************************
函数名:void UpdateServerAll(unsigned short ServerNum)
函数功能:更新所有服务器与网络链接关系
函数变量:ServerNum:服务器个数
函数返回值:最短距离带宽
**********************************************/

void UpdateServerAll(unsigned short ServerNum)
{
    for (int i = 0; i < CurrentGragh.ExpenseNum; i++)
        UpdateSinggleServer(NowServerNum, i);
}


void UpdateSinggleServer(unsigned short ServerNum, unsigned short SingleExpensePoint)
{
    unsigned short Connect_Min = MaxERROR;          //定义与某一点相连的路径最小值
    unsigned short Current_Aspect = 0;          //当前层,或者与当前寻找的次数
    unsigned short Current_Point = 0;               //当前点
    unsigned short SameLegthNum = 0;                //当前层找到的最小路径数量
    unsigned short i = 0;
    unsigned short Sum_Legtgh = 0;              //累计长度
    unsigned short LastNum = 1;                         //一共找到的点数
    unsigned short Start_Point = ExpensePointID[SingleExpensePoint] - 1;
    unsigned short PreID = 0, NowID = 0;
    unsigned short PaiXuNum = 0;

    memset(SearchedPoint, 0, Max_NetNodeNum*sizeof(unsigned short));

    LastPoint[0] = Start_Point;                 //第一次赋值

    for (int n = 0; n < NowServerNum; n++)  //服务器和消费节点直接相连
    {
        if (Start_Point == NowSearchedPath[n])
        {
            ExpenceClosedServer[SingleExpensePoint][0] = 0; //记录离消费节点最近的服务器距离
            ExpenceClosedServer[SingleExpensePoint][1] = NowSearchedPath[n];    //记录离消费节点最近的服务器编号
            return;
        }
    }

    for (Current_Aspect = 0; Current_Aspect < CurrentGragh.NetNodeNum; Current_Aspect++)    //表示是第几次寻找
    {
        for (int n = 0; n < LastNum; n++)
        {
            Current_Point = LastPoint[n];
            if ((SearchedPoint[Current_Point] != 0) || (Current_Point == Start_Point))      //此点已经存在,即已经被搜寻过
            {
                for (int k = 0; k < ConnectNum[Current_Point]; k++)
                {
                    i = ConnectRelation[Current_Point][k];
                    if ((i != Start_Point) && (Connect_Min >= SearchedPoint[Current_Point] + CurrentGragh.Net_Graph[i][Current_Point].SingleCost) && (CurrentGragh.Net_Graph[i][Current_Point].BandWidth_Max != 0) && (SearchedPoint[i] == 0))  //找到这一层的最小值
                    {
                        if (Connect_Min>SearchedPoint[Current_Point] + CurrentGragh.Net_Graph[i][Current_Point].SingleCost) //最小值更新
                        {
                            SameLegthNum = 0;
                        }
                        Connect_Min = SearchedPoint[Current_Point] + CurrentGragh.Net_Graph[i][Current_Point].SingleCost;   //当前最小值
                        ConnectedNum_Pre[SameLegthNum][0] = Current_Point;                  //记录被搜的编号
                        ConnectedNum_Pre[SameLegthNum][1] = i;                              //记录搜到最小值的编号
                        SameLegthPoint[SameLegthNum++] = i;                                 //记录最小值个数以及座标
                    }
                }
            }
        }

        if (SameLegthNum == 0) break;                                                       //没有找到点,退出搜索
        else                                                                                //寻找有没有相同的终点,有的话,走路径最短的那一条
        {
            unsigned short RepeatNum = 0;                       //重复的个数
            memset(RepeatBuf, 0, Max_NetNodeNum*sizeof(unsigned short));
            unsigned short MaxFlow = 0, MaxFlowPoint = 0;
            for (int i = 0; (i < SameLegthNum); i++)                //第几个点
            {
                MaxFlow = CurrentGragh.Net_Graph[SameLegthPoint[i]][ConnectedNum_Pre[i][0]].BandWidth_Max;
                MaxFlowPoint = ConnectedNum_Pre[i][0];
                RepeatNum = 1; RepeatBuf[0] = i;
                for (int j = i + 1; (j < SameLegthNum) && (j != i); j++)
                {
                    if (SameLegthPoint[i] == SameLegthPoint[j]) //两个点是同一个点
                    {
                        if (CurrentGragh.Net_Graph[SameLegthPoint[j]][ConnectedNum_Pre[j][0]].BandWidth_Max > MaxFlow)
                        {
                            MaxFlow = CurrentGragh.Net_Graph[SearchedPoint[j]][ConnectedNum_Pre[j][0]].BandWidth_Max;
                            MaxFlowPoint = ConnectedNum_Pre[j][0];
                        }
                        RepeatBuf[RepeatNum++] = j;//第几个点 
                    }
                }
                for (int k = 0; k < RepeatNum; k++)
                {
                    ConnectedNum_Pre[RepeatBuf[k]][0] = MaxFlowPoint;
                    ConnectedNum_Pre[RepeatBuf[k]][1] = SameLegthPoint[i];
                }
            }
        }
        for (i = 0; i < SameLegthNum; i++)
        {
            SearchedPoint[SameLegthPoint[i]] = Connect_Min;                                 //放入最短长度
            ConnectedLength_Min[SameLegthPoint[i]][Start_Point] = Connect_Min;              //最小距离放入链接数组
            ConnectedMin_ID_Test[Start_Point][ConnectedNum_Pre[i][1]] = ConnectedNum_Pre[i][0];
            LastPoint[LastNum++] = SameLegthPoint[i];                                       //记录已经被找过的点数
            for (int n = 0; n < NowServerNum; n++)
            {
                if (SameLegthPoint[i] == NowSearchedPath[n])    //找到最近的服务器
                {
                    PreID = Start_Point; NowID = NowSearchedPath[n];
                    ExpenceClosedServer[SingleExpensePoint][0] = Connect_Min;   //记录离消费节点最近的服务器距离
                    ExpenceClosedServer[SingleExpensePoint][1] = NowSearchedPath[n];    //记录离消费节点最近的服务器编号
                    while (NowID != Start_Point)
                    {
                        NowID = ConnectedMin_ID_Test[Start_Point][NowID];
                        PaiXu[PaiXuNum++] = NowID;
                    }
                    NowID = Start_Point;
                    while (PaiXuNum > 1)
                    {
                        ConnectedMin_ID[NowSearchedPath[n]][NowID] = PaiXu[PaiXuNum - 2];
                        NowID = PaiXu[PaiXuNum - 2];
                        PaiXuNum--;
                    }
                    ConnectedMin_ID[NowSearchedPath[n]][NowID] = NowSearchedPath[n];
                    return;
                }
            }
        }
        SameLegthNum = 0; Connect_Min = MaxERROR; PaiXuNum = 0;                                             //累计清零
    }
}


/*********************************************
函数名:void SatifyPoint(unsigned short expenseID, unsigned short servernumed)
函数功能:满足特定消费节点需要
函数变量:servernumed:服务器个数  expenseID:消费节点标号 0-N
函数返回值:null
**********************************************/
void SatifyPoint(unsigned short expenseID, unsigned short servernumed)
{
    unsigned short serverID = 0;
    unsigned short MiniFlow = 0;
    static unsigned short temp = 0;

    while (CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + expenseID][ExpensePointID[expenseID] - 1].BandWidth_Need != 0)//判断当前节点是否被满足
    {
        if (temp == CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + expenseID][ExpensePointID[expenseID] - 1].BandWidth_Need)
        {
            UnsatifiedPoint[UnsatifiedNum++] = expenseID;
            break;
        }
        temp = CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + expenseID][ExpensePointID[expenseID] - 1].BandWidth_Need;
        serverID = MinPoint(servernumed, ExpensePointID[expenseID] - 1);        //获得最短路径的服务器编号
        MiniFlow = GetMinFlow(serverID, expenseID);         //获得改路径上的最小带宽
        UpdateGraph(MiniFlow, serverID, expenseID);         //满足更新
        NetPathALL++;                                       //路径累计
    }
}


/*********************************************
函数名:unsigned short  GetCharge(void)
函数功能:获得所有费用
函数变量:void
函数返回值:null
**********************************************/
unsigned long GetCharge(char * TextFile, bool IsPrint)
{
    unsigned long ChargeALL_Temp = 0;
    unsigned short PreID = 0;
    unsigned short NowID = 0;
    ChargeALL_Temp += NowServerNum*CurrentGragh.ServerCost;//获得服务器费用

    if (IsPrint == true) PRINT("总条数:%d\n", NetPathALL);

    for (int i = 0; i < CurrentGragh.ExpenseNum; i++)   //消费节点
    {
        for (int j = 0; j < Path_Num[i][0]; j++)        //消费节点边数
        {
            PreID = ExpensePointID[i] - 1;//消费节点ID
            if (IsPrint == true) PRINT("%d ", i);
            if (IsPrint == true) PRINT("%d ", PreID);
            for (int k = 0; Path[i][j][k] != -1; k++)
            {
                NowID = Path[i][j][k];
                ChargeALL_Temp += CurrentGragh.Net_Graph[NowID][PreID].SingleCost*PathExpense[i][j];
                PreID = NowID;
                if (IsPrint == true) PRINT("%d ", Path[i][j][k]);
            }
            if (IsPrint == true) PRINT("流量:%d\n", PathExpense[i][j]);
        }
    }
    if (IsPrint == true) PRINT("总费用:%d\n", ChargeALL_Temp);
    return ChargeALL_Temp;
}

/*********************************************
函数名:void UpdateMaxie(void)
函数功能:更新矩阵
函数变量:void
函数返回值:null
**********************************************/
void UpdateMaxie(void)
{
    int i = 0, j = 0;
    for (i = 0; i < CurrentGragh.ExpenseNum + CurrentGragh.NetNodeNum; i++)
    {
        if (i < CurrentGragh.ExpenseNum) { Path_Num[i][0] = 0; Path_Num[i][1] = 0; }//路径数和路径上点数清零
        for (j = 0; j < CurrentGragh.ExpenseNum + CurrentGragh.NetNodeNum; j++)
        {
            ConnectedLength_Min[i][j] = ConnectedLength_Min_Temp[i][j];
            ConnectedMin_ID[i][j] = ConnectedMin_ID_Temp[i][j];
        }
    }
    for (i = 0; i < BandWidthNum; i++)
    {
        CurrentGragh.Net_Graph[BandWidth[i][0]][BandWidth[i][1]].BandWidth_Max = BandWidth[i][2];
        CurrentGragh.Net_Graph[BandWidth[i][1]][BandWidth[i][0]].BandWidth_Max = BandWidth[i][2];
        CurrentGragh.Net_Graph[BandWidth[i][0]][BandWidth[i][1]].SingleCost = BandWidth[i][3];
        CurrentGragh.Net_Graph[BandWidth[i][1]][BandWidth[i][0]].SingleCost = BandWidth[i][3];
    }
    for (i = 0; i < CurrentGragh.ExpenseNum; i++)
    {
        CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + i][ExpensePointID[i] - 1].BandWidth_Need = ExpenseBandNeed[i];
        CurrentGragh.Net_Graph[ExpensePointID[i] - 1][CurrentGragh.NetNodeNum + i].BandWidth_Need = ExpenseBandNeed[i];
    }
}

/*********************************************
函数名:void GetMaxFlow(void)
函数功能:获得最大流量和最小费用
函数变量:void
函数返回值:null
**********************************************/

unsigned short SameLengthBuf[Max_NetNodeNum] = { 0 };
bool GetMaxFlow(void)
{
    unsigned long MaxFlowALL = 0;
    unsigned short MinPath = MaxERROR;
    unsigned short MinPathTemp = MaxERROR;
    unsigned short ServerID = 0, ExpenseID = 0;
    unsigned long CurrentFlow = 0;
    unsigned long SumFlow = 0;
    unsigned long PreSumFlow = 0;


    unsigned long MaxFlow = 0;
    unsigned long MaxFlowTemp = 0;
    unsigned short SameLengthNum = 0;//相同长度累计


    NetPathALL = 0;//所有路径清零
    for (int i = 0; i < CurrentGragh.ExpenseNum; i++) MaxFlowALL += CurrentGragh.Net_Graph[i + CurrentGragh.NetNodeNum][ExpensePointID[i] - 1].BandWidth_Need;
    while (SumFlow != MaxFlowALL)
    {
        MinPath = MaxERROR;

        for (int i = 0; i < CurrentGragh.ExpenseNum; i++)   //找到最短路径和编号
        {
            MinPathTemp = ConnectedLength_Min[ExpenceClosedServer[i][1]][ExpensePointID[i] - 1];    //消费节点到服务器最短路径
            if ((MinPathTemp <= MinPath) && (CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + i][ExpensePointID[i] - 1].BandWidth_Need != 0))
            {
                if (MinPathTemp < MinPath) SameLengthNum = 0;
                MinPath = MinPathTemp;//赋值
                ServerID = ExpenceClosedServer[i][1]; ExpenseID = i;    //保存服务器编号和消费节点编号0-N
                SameLengthBuf[SameLengthNum++] = i;
            }
        }

        MaxFlow = 0; MaxFlowTemp = 0;
        for (int k = 0; k < SameLengthNum; k++) //在最短路径中选择流量量大或最小
        {
            unsigned short i = SameLengthBuf[k];
            if ((ExpenceClosedServer[i][0] == MinPath) && (CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + i][ExpensePointID[i] - 1].BandWidth_Need != 0)) //是最短路径时
            {
                MaxFlowTemp = GetMinFlow(ExpenceClosedServer[i][1], i); //获取最近的服务器流量
                if (MaxFlowTemp != MaxERROR)
                {
                    if (MaxFlowTemp>CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + i][ExpensePointID[i] - 1].BandWidth_Need)
                        MaxFlowTemp = CurrentGragh.Net_Graph[CurrentGragh.NetNodeNum + i][ExpensePointID[i] - 1].BandWidth_Need;
                }
                if ((MaxFlowTemp > MaxFlow) && (MaxFlowTemp != MaxERROR) && (MaxFlowTemp != 0))
                {
                    MaxFlow = MaxFlowTemp;
                    ServerID = ExpenceClosedServer[i][1]; ExpenseID = i;//保存服务器编号和消费节点编号0-N
                }
            }
        }
        //PRINT("路径服务器%d,消费节点%d\n", ServerID, ExpenseID);
        if (MinPath != MaxERROR)
        {
            CurrentFlow = GetMinFlow(ServerID, ExpenseID);          //获得改路径上的最小带宽
            CurrentFlow = UpdateGraph(CurrentFlow, ServerID, ExpenseID);            //满足更新
            if (CurrentFlow != 0)           NetPathALL++;           //路径累计
            //CurrentFlow=UpdateGraph(CurrentFlow, ServerID, ExpenseID);
            SumFlow += CurrentFlow;//当前累计流量
            if (SumFlow == PreSumFlow) { return false; }
            PreSumFlow = SumFlow;
        }
    }
    return true;
}

/*********************************************
函数名:void GetSinglePoint(void)
函数功能:获得系统中的孤立节点
函数变量:void
函数返回值:null
**********************************************/
unsigned short CurrentBuf[Max_NetNodeNum][2] = { 0 };
void GetSinglePoint(void)
{
    memset(CurrentBuf, 0, 2*Max_NetNodeNum*sizeof(unsigned short));

    for (int i = 0; i < CurrentGragh.ExpenseNum; i++)   //当链接的消费节点线路的流量*单位费用>一个服务器单价的时候,直接放一个服务器在该消费节点
    {
        unsigned long ChargeExpense = 0;    //当前花费
        unsigned short CurrentFlow = 0;     //当前流量
        unsigned short CurrentNeed = CurrentGragh.Net_Graph[i + CurrentGragh.NetNodeNum][ExpensePointID[i] - 1].BandWidth_Need;     //当前需要流量
        unsigned short CurentTemp = 0;
        for (int j = 0; j < ConnectNum[ExpensePointID[i] - 1]; j++) //当前所有连接的消费节点
        {
            CurrentBuf[j][0] = CurrentGragh.Net_Graph[ExpensePointID[i] - 1][ConnectRelation[ExpensePointID[i] - 1][j]].SingleCost;
            CurrentBuf[j][1] = ConnectRelation[ExpensePointID[i] - 1][j];
        }
        //MaoPao(CurrentBuf, ConnectNum[ExpensePointID[i] - 1],);//路径,单价从小到大排序\
                //冒泡法start
        int ii, jj;
        unsigned short Temp = 0;//存放最小值
        unsigned short idTemp = 0;
        for (ii = 0; ii < ConnectNum[ExpensePointID[i] - 1]; ii++)
        {
            for (jj = ii + 1; jj < ConnectNum[ExpensePointID[i] - 1]; jj++)
            {
                if ((CurrentBuf[ii][0]>CurrentBuf[jj][0]))
                {

                    Temp = CurrentBuf[ii][0];
                    CurrentBuf[ii][0] = CurrentBuf[jj][0];
                    CurrentBuf[jj][0] = Temp;
                    idTemp = CurrentBuf[ii][1];
                    CurrentBuf[ii][1] = CurrentBuf[jj][1];
                    CurrentBuf[jj][1] = idTemp;
                }
            }
        }
        //冒泡法End
        for (int j = 0; j < ConnectNum[ExpensePointID[i] - 1]; j++)         //排序后得到最短钱数
        {
            if (CurrentFlow < CurrentGragh.Net_Graph[i + CurrentGragh.NetNodeNum][ExpensePointID[i] - 1].BandWidth_Need)
            {
                CurentTemp = (CurrentGragh.Net_Graph[ExpensePointID[i] - 1][CurrentBuf[j][1]].BandWidth_Max> CurrentNeed) ? CurrentNeed : CurrentGragh.Net_Graph[ExpensePointID[i] - 1][CurrentBuf[j][1]].BandWidth_Max;
                CurrentFlow += CurentTemp;
                ChargeExpense += CurentTemp*CurrentGragh.Net_Graph[ExpensePointID[i] - 1][CurrentBuf[j][1]].SingleCost;
                CurrentNeed -= CurentTemp;
            }
        }
        if ((ChargeExpense > CurrentGragh.ServerCost) || (CurrentFlow<CurrentGragh.Net_Graph[i + CurrentGragh.NetNodeNum][ExpensePointID[i] - 1].BandWidth_Need))
        {
            SinglePoint[SinglePointNum++] = i;//放入孤立节点,直接把服务器放在这
        }
    }
}

void DivisionBlock(void)
{
    unsigned short TempPoint = 0;
    unsigned short TempNumExpense = 0;
    unsigned short TempNumNode = 0;
    unsigned short temp = 0;
    int k = 0;
    for (int i = 0; i < CurrentGragh.ExpenseNum; i++)               //遍历每个消费节点
    {
        TempPoint = ExpensePointID[i] - 1;

        ConnectedNodePoint[i][TempNumExpense++] = TempPoint;        //把与消费节点绑定的普通节点记录下来

        ConnectedExpensePoint[TempPoint][ConnectedExpensePointNum[TempPoint]++] = i;        //把普通节点与哪些消费节点有关系记录下来
        for (int j = 0; j < 2; j++)                                 //每个消费节点遍历深度为2
        {
            for (k = 0; k < CurrentGragh.NetNodeNum; k++)
            {
                if (CurrentGragh.Net_Graph[TempPoint][k].BandWidth_Max != 0)
                {
                    ConnectedNodePoint[i][TempNumExpense++] = k;
                    ConnectedExpensePoint[k][ConnectedExpensePointNum[k]++] = i;
                }
            }
            TempPoint = k;
        }
        ConnectedNodePointNum[i] = TempNumExpense;
        //ConnectedExpensePointNum[]
        TempNumExpense = 0;
        TempNumNode = 0;
    }

    /*上面把消费节点遍历了一遍*/
    /*下面的部分为划分成不同的小块*/
    PreServerNum = 0; NowServerNum = 0;
    /*得到每个普通节点与其他节点的大概关系*/
    for (int j = 0; j < CurrentGragh.NetNodeNum; j++)
    {
        if (ConnectedExpensePointNum[j] != 0)
        {
            for (int p = 0; p < ConnectedExpensePointNum[j]; p++)
            {
                temp += ConnectedNodePointNum[ConnectedExpensePoint[j][p]];

            }
        }
        NodeNum[j][0] = temp;
        NodeNum[j][1] = j;
        temp = 0;
    }

    int i = 0, j = 0;
    unsigned short Temp = 0;//存放最小值
    unsigned short idTemp = 0;
    for (i = 0; i < CurrentGragh.NetNodeNum; i++)
    {
        for (j = i + 1; j < CurrentGragh.NetNodeNum; j++)
        {
            if (NodeNum[i][0] < NodeNum[j][0])
            {
                Temp = NodeNum[i][0];
                NodeNum[i][0] = NodeNum[j][0];
                NodeNum[j][0] = Temp;
                idTemp = NodeNum[i][1];
                NodeNum[i][1] = NodeNum[j][1];
                NodeNum[j][1] = idTemp;
            }
        }
    }
}



/*********************************************
函数名:void GetExpenseMin(void)
函数功能:获得系统中的消费节点最短距离
函数变量:void
函数返回值:null
**********************************************/
void GetExpenseMin(void)
{
    short i = 0, j = 0;
    short MinLength = MaxERROR;//最小距离
    short CurrentID = MaxERROR;
    memset(ExpensePointSum, 0, Max_NetNodeNum * 2 * sizeof(unsigned short));
    for (i = 0; i < CurrentGragh.ExpenseNum; i++)   //当前消费节点
    {
        MinLength = MaxERROR; CurrentID = MaxERROR;
        for (j = 0; (j < CurrentGragh.ExpenseNum); j++) //与这比较的消费节点
        {
            if ((i != j) && (MinLength>ConnectedLength_Min[ExpensePointID[i] - 1][ExpensePointID[j] - 1]) && (ConnectedLength_Min[ExpensePointID[i] - 1][ExpensePointID[j] - 1] != 0))
            {
                MinLength = ConnectedLength_Min[ExpensePointID[i] - 1][ExpensePointID[j] - 1];
                CurrentID = j;
            }
        }
        if ((MinLength != 0) && (MinLength != MaxERROR))
        {
            unsigned short PreID = ExpensePointID[i] - 1;           //链接的前一个点   
            unsigned short NowID = ExpensePointID[CurrentID] - 1;           //当前点
            ExpensePointSum[NowID][0]++;
            ExpensePointSum[NowID][1] = NowID;
            while (NowID != ExpensePointID[i] - 1)              //搜寻到初始点为止
            {
                PreID = ConnectedMin_ID[ExpensePointID[i] - 1][NowID];
                NowID = PreID;                                                    //循环赋值
                ExpensePointSum[NowID][0]++;
                ExpensePointSum[NowID][1] = NowID;
            }
        }
    }

    //冒泡排序
    unsigned short Temp = 0;//存放最小值
    unsigned short idTemp = 0;
    for (i = 0; i < CurrentGragh.NetNodeNum; i++)
    {
        for (j = i + 1; j < CurrentGragh.NetNodeNum; j++)
        {

            if ((ExpensePointSum[i][0]<ExpensePointSum[j][0]))
            {

                Temp = ExpensePointSum[i][0];
                ExpensePointSum[i][0] = ExpensePointSum[j][0];
                ExpensePointSum[j][0] = Temp;
                idTemp = ExpensePointSum[i][1];
                ExpensePointSum[i][1] = ExpensePointSum[j][1];
                ExpensePointSum[j][1] = idTemp;
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章