2017 Huawei Codecraft Intermediary


代码版权归大赛组委会所有,请勿转载!

4 月 25 号晚 9 点,复赛也完美谢幕,最终取得杭厦赛区第二的佳绩,代码量与初赛相当,修改了费用流的算法,不再采用 zkw,采用速度更快的网络单纯性算法(Network Simplex Algorithm),代码包已上传,下载地址:

2017-Huawei-Codecraft-Intermediary-CodePackage-Download

fusai_rankresult

第二个中级用例获得了满分,赛区第一,第一个终极用例赛区第二,第一个高级用例赛区第三,第二个高级用例比较渣,赛区第七

fusai_costresult

复赛的题目在初赛题目的基础上添加了服务器的购买费用,不同档次的服务器输出带宽不同,输出能力越强的服务器的硬件成本也越高,此外网络结点的部署费用也不再单一了,有高有低,复赛题目与初赛题目完整的差别描述如下:

2017-huawei-codecraft-intermediary-contest-problem


~~~~~    华丽丽的分割线    ~~~~~

代码工程的 Makefile 与初赛的完全一致,下面只粘贴和初赛代码有较大差异的代码文件(缩进还是有问题,不知为啥,不打算调整了,将就着看吧)。

Utils.h:

/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd.                                          *
 * @file: Utils.h                                                                    *
 * @author: Xu Le                                                                    *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#ifndef __UTILS_H__
#define __UTILS_H__

#include <cstdint>
#include <cstring>
#include <vector>
#include <list>
#include <stack>
#include <queue>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <utility>

#include "Timer.h"

#define square(x)    ((x)*(x))

/** structure used to store input lines' parsed result. */
struct LinkTuple
{
    LinkTuple(int _src, int _dst, int _bandwidth, int _rental) : src(_src), dst(_dst), bandwidth(_bandwidth), rental(_rental) {}

    LinkTuple(const LinkTuple& rhs) = default;
    LinkTuple& operator=(const LinkTuple& rhs) = default;

    int src;       ///< first element in a single input line.
    int dst;       ///< second element in a single input line.
    int bandwidth; ///< link bandwidth.
    int rental;    ///< link rental fee.
};

/** case complexity enum value space. */
enum class Complexity {
    Oversimplified,
    Simple,
    General,
    Hard,
    VeryHard,
    UltraHard,
    Hell
};

/** parse input lines from case file. */
void parseInput(char *filename, std::list<struct LinkTuple>& linkTuples);

/** output flux path list to result file. */
void outputToFile(char *filename, std::list<std::vector<int> >& fluxPathList);

/** output NA to result file. */
void outputToFile(char *filename);

/** binary search in a certain std::vector, cost O(log n) */
bool binarySearch(const std::vector<int>& vec, const int key);

/** determine the highest server level to be used. */
void defineAvailableLevel();

/**
 * locate the server level of which a network node should deployed according to node's connected link capacity,
 * interval of nodeCap locate in [ oupcapTable[x], oupcapTable[x+1] ), note the function return 0
 * when nodeCap < outcapTable[0], cost O(log n).
 */
int locateLevel(const int nodeCap);

/**
 * fit the server level of which a network node should deployed according to node's actual output flux obtained by MCF,
 * interval of outputFlux locate in ( oupcapTable[x], oupcapTable[x+1] ], note the function return hardwareLevelNum
 * when nodeCap > outcapTable[hardwareLevelNum-1], cost O(log n).
 */
int fitLevel(const int outputFlux);

/** obtain the total purchase fee under the condition that all chosen network nodes deploy fitted server level. */
long obtainTotalPurchaseFee(std::vector<int>& serversOutputFlux, size_t serverNum);

/** define the default complexity of the case according to its size. */
Complexity defineDefaultComplexity();

/** convert enum value to its name string, only useful for debuging purpose. */
const char* complexityString(Complexity complexity);

////////////////    convenient functions of std::vector and std::list    ////////////////
void printVector(std::vector<int>& _vector, const char *_note);

void printVector(std::vector<double>& _vector, const char *_note);

void printList(std::list<int>& _list, const char *_note);

void printList(std::list<double>& _list, const char *_note);

#endif /* __UTILS_H__ */

Utils.cpp:

/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd.                                          *
 * @file: Utils.cpp                                                                  *
 * @author: Xu Le                                                                    *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#include "Utils.h"

extern int log_level;

extern int maxUnitRentalFee;

extern uint32_t networkNodeNum;
extern uint32_t linkNum;
extern uint32_t consumptionNodeNum;
extern uint32_t hardwareLevelNum;

extern std::vector<int> priceTable;
extern std::vector<int> outcapTable;
extern std::vector<int> deployTable;
extern std::vector<int> accessTable;
extern std::vector<int> demandTable;
extern std::unordered_map<int, int> provideTable;

void parseInput(char *filename, std::list<struct LinkTuple>& linkTuples)
{
    FILE *fd = fopen(filename, "r");
    if (fd == NULL)
    {
        error_log("Fail to open file %s.\n", filename);
        exit(EXIT_FAILURE);
    }

    uint32_t i = 0;
    int srcNode = 0, dstNode = 0, bandwidth = 0, rental = 0;
    int deployFee = 0;
    int level = 0, outcap = 0, price = 0;
    int cspID = 0, netID = 0, demand = 0;

    fscanf(fd, "%u %u %u\n", &networkNodeNum, &linkNum, &consumptionNodeNum);

    char buf[100] = { '\0' };
    while (true)
    {
        fgets(buf, 100, fd);
        if (buf[0] == '\r' || buf[0] == '\n')
            break;
        sscanf(buf, "%d %d %d", &level, &outcap, &price);
        outcapTable.push_back(outcap);
        priceTable.push_back(price);
        ++hardwareLevelNum;
    }

    deployTable.resize(networkNodeNum);
    accessTable.resize(consumptionNodeNum);
    demandTable.resize(consumptionNodeNum);

    for (i = 0; i < networkNodeNum; ++i)
    {
        fscanf(fd, "%d %d", &netID, &deployFee);
        deployTable[netID] = deployFee;
    }

    fgetc(fd);

    for (i = 0; i < linkNum; ++i)
    {
        fscanf(fd, "%d %d %d %d", &srcNode, &dstNode, &bandwidth, &rental);
        linkTuples.emplace_back(srcNode, dstNode, bandwidth, rental);
        if (maxUnitRentalFee < rental)
            maxUnitRentalFee = rental;
    }

    fgetc(fd);

    for (i = 0; i < consumptionNodeNum; ++i)
    {
        fscanf(fd, "%d %d %d", &cspID, &netID, &demand);
        accessTable[cspID] = netID;
        demandTable[cspID] = demand;
    }

    for (i = 0; i < consumptionNodeNum; ++i)
        provideTable.insert(std::pair<int, int>(accessTable[i], i));

    fclose(fd);
}

void outputToFile(char *filename, std::list<std::vector<int> >& fluxPathList)
{
    FILE *fd = fopen(filename, "w");
    if (fd == NULL)
    {
        error_log("Fail to open file %s.\n", filename);
        exit(EXIT_FAILURE);
    }

    fprintf(fd, "%u\n\n", static_cast<uint32_t>(fluxPathList.size()));

    for (std::list<std::vector<int> >::iterator iter = fluxPathList.begin(); iter != fluxPathList.end(); ++iter)
    {
        std::vector<int> &fluxPath = *iter; // alias
        for (size_t i = 0; i < fluxPath.size()-1; ++i)
            fprintf(fd, "%d ", fluxPath[i]);
        fprintf(fd, "%d\n", fluxPath.back());
    }

    fclose(fd);
}

void outputToFile(char *filename)
{
    FILE *fd = fopen(filename, "w");
    if (fd == NULL)
    {
        error_log("Fail to open file %s.\n", filename);
        exit(EXIT_FAILURE);
    }

    fprintf(fd, "NA\n");

    fclose(fd);
}

bool binarySearch(const std::vector<int>& vec, const int key)
{
    int low = 0, high = static_cast<int>(vec.size())-1, mid = 0;
    while (low <= high)
    {
        mid = low + ((high - low) >> 1);
        if (key == vec[mid])
            return true;
        else if (key < vec[mid])
            high = mid - 1;
        else
            low = mid + 1;
    }
    return false;
}

void defineAvailableLevel()
{
    uint32_t i = 0;
    double sixthPosDeployFee = 0.0;
    std::vector<double> unitOutputCapFee(hardwareLevelNum, 0.0);
    std::vector<double> sortDeployFee;
    sortDeployFee.reserve(networkNodeNum);
    for (i = 0; i < networkNodeNum; ++i)
        sortDeployFee.push_back(deployTable[i]);

    std::sort(sortDeployFee.begin(), sortDeployFee.end());

    sixthPosDeployFee = sortDeployFee[networkNodeNum/6];
    info_log("sixth position deploy fee: %f\n", sixthPosDeployFee);

    for (i = 0; i < hardwareLevelNum; ++i)
    {
        unitOutputCapFee[i] = (priceTable[i] + sixthPosDeployFee) / outcapTable[i];
        info_log("level [%u]'s unit output capacity fee is %f\n", i, unitOutputCapFee[i]);
    }

    const uint32_t oldHardwareLevelNum = hardwareLevelNum;
    const uint32_t L = 4;
    if (hardwareLevelNum > L) // 5, 6, 7, 8, 9, 10
    {
        double *deltaUnitCapFee = new double[hardwareLevelNum-L];
        double *deltaFeeRatio = NULL;
        if (hardwareLevelNum > L+1)
            deltaFeeRatio = new double[hardwareLevelNum-L-1];
        for (i = L; i < hardwareLevelNum; ++i)
            deltaUnitCapFee[i-L] = unitOutputCapFee[i] - unitOutputCapFee[i-1];
        for (i = 0; i < hardwareLevelNum-L; ++i)
            info_log("deltaUnitCapFee[%u]: %f\n", i+L-1, deltaUnitCapFee[i]);
        if (hardwareLevelNum > L+1)
        {
            uint32_t maxDeltaIdx = 0;
            double maxDeltaFeeRatio = -100.0;
            for (i = 0; i < hardwareLevelNum-L-1; ++i)
            {
                deltaFeeRatio[i] = deltaUnitCapFee[i+1] - deltaUnitCapFee[i];
                if (deltaFeeRatio[i] < 0)
                    deltaFeeRatio[i] = -deltaFeeRatio[i];
                if (maxDeltaFeeRatio < deltaFeeRatio[i])
                {
                    maxDeltaFeeRatio = deltaFeeRatio[i];
                    maxDeltaIdx = i+L-1;
                }
                info_log("deltaFeeRatio[%u]: %f\n", i+L-1, deltaFeeRatio[i]);
            }
            hardwareLevelNum = maxDeltaIdx + 2;
            if (hardwareLevelNum > oldHardwareLevelNum)
                hardwareLevelNum = oldHardwareLevelNum;
            uncond_log("highest available server level limited to %u\n", hardwareLevelNum-1);
            delete []deltaFeeRatio;
        }
        delete []deltaUnitCapFee;
    }
}

int locateLevel(const int nodeCap)
{
    int low = 0, high = static_cast<int>(hardwareLevelNum), mid = 0;
    while (high - low > 1)
    {
        mid = low + ((high - low) >> 1);
        if (nodeCap < outcapTable[mid])
            high = mid;
        else
            low = mid;
    }
    return low;
}

int fitLevel(const int outputFlux)
{
    if (outputFlux > outcapTable[hardwareLevelNum-1])
        return hardwareLevelNum;

    int low = -1, high = static_cast<int>(hardwareLevelNum)-1, mid = 0;
    while (high - low > 1)
    {
        mid = low + ((high - low) >> 1);
        if (outputFlux <= outcapTable[mid])
            high = mid;
        else
            low = mid;
    }
    return high;
}

long obtainTotalPurchaseFee(std::vector<int>& serversOutputFlux, size_t serverNum)
{
    long totalPurchaseFee = 0;

    for (size_t i = 0; i < serverNum; ++i)
    {
        if (serversOutputFlux[i] > 0)
        {
            int level = fitLevel(serversOutputFlux[i]);
            totalPurchaseFee += priceTable[level];
        }
    }

    return totalPurchaseFee;
}

Complexity defineDefaultComplexity()
{
    if (consumptionNodeNum > 300)
        return Complexity::Hell;
    if (consumptionNodeNum > 200 || networkNodeNum > 800)
        return Complexity::UltraHard;
    if (consumptionNodeNum > 100 || networkNodeNum > 500)
        return Complexity::VeryHard;
    if (consumptionNodeNum > 50 || networkNodeNum > 300)
        return Complexity::Hard;
    if (consumptionNodeNum > 20 || networkNodeNum > 100)
        return Complexity::General;
    if (consumptionNodeNum > 10 || networkNodeNum > 50)
        return Complexity::Simple;
    return Complexity::Oversimplified;
}

const char* complexityString(Complexity complexity)
{
    switch (complexity)
    {
    case Complexity::Oversimplified:
        return "oversimplified";
    case Complexity::Simple:
        return "simple";
    case Complexity::General:
        return "general";
    case Complexity::Hard:
        return "hard";
    case Complexity::VeryHard:
        return "very-hard";
    case Complexity::UltraHard:
        return "ultra-hard";
    default:
        return "hell";
    }
}

////////////////    convenient functions of std::vector and std::list    ////////////////
void printVector(std::vector<int>& _vector, const char *_note)
{
    if (log_level >= INFO_LEVEL)
    {
        printf("%s", _note);
        for (std::vector<int>::iterator iter = _vector.begin(); iter != _vector.end(); ++iter)
            printf("%d ", *iter);
        printf("\n");
    }
}

void printVector(std::vector<double>& _vector, const char *_note)
{
    if (log_level >= INFO_LEVEL)
    {
        printf("%s", _note);
        for (std::vector<double>::iterator iter = _vector.begin(); iter != _vector.end(); ++iter)
            printf("%f ", *iter);
        printf("\n");
    }
}

void printList(std::list<int>& _list, const char *_note)
{
    if (log_level >= INFO_LEVEL)
    {
        printf("%s", _note);
        for (std::list<int>::iterator iter = _list.begin(); iter != _list.end(); ++iter)
            printf("%d ", *iter);
        printf("\n");
    }
}

void printList(std::list<double>& _list, const char *_note)
{
    if (log_level >= INFO_LEVEL)
    {
        printf("%s", _note);
        for (std::list<double>::iterator iter = _list.begin(); iter != _list.end(); ++iter)
            printf("%f ", *iter);
        printf("\n");
    }
}

Graph.h:

/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd.                                          *
 * @file: Graph.h                                                                    *
 * @author: Xu Le, He Jianfan                                                        *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#ifndef __GRAPH_H__
#define __GRAPH_H__

#include "Utils.h"

#define INF    0x7fffffff

enum class ArcStatus {
    T, ///< in T, 0 < arc.flow < bandwidth
    L, ///< in L, arc.flow == 0
    U  ///< in U, arc.flow == bandwidth
};

enum class MCFStatus {
    Unsolved,   ///< optimal solution has not be found now.
    Solved,     ///< has found the optimal solution of problem.
    Unfeasible, ///< problem is unfeasible.
    Unbounded   ///< problem is unbounded.
};

/** arc structure of graph. */
struct Arc
{
    Arc();

    int arcID;           ///< ID of this arc.
    int srcID;           ///< tail of this arc.
    int dstID;           ///< head of this arc.
    int bandwidth;       ///< max capacity of this arc.
    int rental;          ///< cost weight of this arc.
    int flow;            ///< initial flow of this arc.
    ArcStatus status;    ///< tells if arc is deleted, closed, in T, L, or U.

    struct Arc *nextArc; ///< next out degree arc of the node this arc derived.
};

/** node structure of graph. */
struct Node
{
    Node();

    int potential;     ///< the node potential corresponding with the flow conservation constrait of this node.
    int treeLevel;     ///< the depth of the node in T as to T root.
    int restrictedCap; ///< restricted output capacity due to imposed server's hardware level limitation.

    struct Node *prev; ///< previous node in the order of the Post-Visit on T.
    struct Node *next; ///< next node in the order of the Post-Visit on T.

    struct Arc *enterArc; ///< entering tree arc of this node.
    struct Arc *dummyArc; ///< artificial arcs are used to connect nodes with the dummy root node.

    struct Arc *firstArc; ///< first out degree arc of this node.
    struct Arc *lastArc;  ///< last out degree arc of this node.
};

/** candidate structure used in minimum cost flow algorithm. */
struct Candidate
{
    Candidate() : absReductC(0), arc(NULL) {}

    int absReductC;  ///< absolute value of the arc's reduced cost.
    struct Arc *arc; ///< pointer to the violating primal bound arc.
};

/** adjacency list structure representing the graph. */
class Graph
{
public:
    explicit Graph(uint32_t _node_num);
    ~Graph();

    /** @name regular methods of Graph. */
    ///@{
    void construct(std::list<struct LinkTuple>& linkTuples);
    void undirectify(std::list<struct LinkTuple>& linkTuples);
    void destruct();
    void display();
    ///@}

    /** @name API function members used by minimum cost flow algorithm. */
    ///@{
    void addSuperSource(std::list<int>& servers);
    void delSuperSource();
    void displaySuperSource();
    void preMinimumCostFlow();
    long minimumCostFlow(std::vector<int>& serversOutputFlux);
    void postMinimumCostFlow();
    void recordFlowPath();
    ///@}

    /** @name API member functions used by TopologyScanner. */
    ///@{
    void BFS(int src);
    void dijkstra(int src);
    void recordNodePath(std::vector<int>& path, int src, int dst);
    long recordArcPath(std::vector<struct Arc*>& path, int src, int dst);
    ///@}

public:
    /** struct used by exchange operation in Greedy algorithm. */
    class FluxFee
    {
    public:
        int from;
        int to;
        int fee;

        bool operator<(FluxFee rhs) const { return fee < rhs.fee; }
    };

    /** @name API member functions used by Greedy. */
    ///@{
    void setExchangeOperation(bool _exchange_operation);
    void setOutputFluxUpbound(uint32_t nodeIdx, int upbound);
    ///@}

private:
    enum Colortype
    {
        White = 0, ///< denote this node has not been visited.
        Gray,      ///< denote this node has been visited but its all out degrees hasn't been visited.
        Black      ///< denote this node's all out degrees has been visited.
    };

    Graph(const Graph&);
    Graph& operator=(const Graph&);

    /** @name internal member functions used by minimum cost flow algorithm. */
    ///@{
    struct Arc* _executeCandidateListPivot();
    void _sortCandidateList(const uint32_t min, const uint32_t max);
    int _reductCost(struct Arc *parc);
    int _parent(int index, struct Arc *parc);
    void _updateTree(struct Arc *k, const int h, const int k1, const int k2);
    struct Node* _cutAndUpdateSubtree(struct Node *root, const int delta);
    void _pasteSubtree(struct Node *root, struct Node *lastNode, struct Node *prevNode);

    void _addSuperSink();
    void _delSuperSink();
    void _revertStatusToInitialization();
    bool _DFS(int s, int t, std::vector<int>& path, int tmpFlow);
    ///@}

public:
    uint32_t nodeNum;       ///< maintaining the node number of the graph.
    uint32_t arcNum;        ///< maintaining the arc number of the graph.
    struct Node *NodeList;  ///< storing all the nodes in the graph.
    int *dist;              ///< recording the time when a node is discovered.
    int *fluxThroughNode;   ///< used by Greedy in exchange operation.
    std::priority_queue<FluxFee> fluxFeeQueue; ///< used by Greedy.
    std::list<std::vector<int> > fluxPathList; ///< stores all flux paths from super source to super sink, which is certainly the result of this program.

private:
    enum Colortype *color;  ///< recording a node's visited status when BFS or DFS traverse is called.
    int *pi;                ///< maintaining relationship of node's predecessor when BFS, DFS or dijkstra traverse is called.
    struct Arc **arcRecord; ///< maintaining relationship of arc's predecessor when to call recordArcPath method after BFS or dijkstra traverse is called.

    /** @name data members used by minimum cost maximum flow algorithm. */
    ///@{
    MCFStatus mcfStatus;          ///< maintaining MCF status

    const uint32_t superSource;   ///< the index of super source node, which is nodeNum.
    const uint32_t superSink;     ///< the index of super sink node, which is nodeNum + 1.
    const uint32_t dummyRoot;     ///< the index of dummy root node, which is nodeNum + 2.
    const uint32_t sinkArcs;      ///< start index of arcs pointing to super sink node.
    const uint32_t sourceArcs;    ///< start index of arcs pointing from super source node.
    const uint32_t dummyArcs;     ///< start index of artificial dummy arcs.
    const uint32_t endDummyArcs;  ///< beyond last index of artificial dummy arcs.

    struct Candidate *candidate;  ///< every element points to an element of the arcs vector which contains an arc violating dual bound
    uint32_t groupNum;            ///< number of the candidate lists
    uint32_t groupPos;            ///< contains the actual candidate list
    uint32_t tmpCandidateListNum; ///< hot list dimension (it is variable)
    uint32_t candidateListNum;    ///< number of candidate lists
    uint32_t hotListNum;          ///< number of candidate lists and hot list dimension

    bool exchangeOperation; ///< indicate whether the exchange operation in greedy algorithm has begun.
    FluxFee *fluxFeeArray;  ///< used by Greedy.
    FluxFee **feeMatrix;    ///< used by Greedy.
    int sumFlux;            ///< sum flux in graph, which is equals to sum up demand flux of all consumption node.
    int minFlow;            ///< used by _DFS function as a global variable.
    uint32_t sourceNum;     ///< stores the number of chosen nodes deployed with servers to maintain arcNum of graph.
    ///@}
};

bool verifyScheme(Graph& graph, long& totalRentalFee, long& totalPurchaseFee);

#endif /* __GRAPH_H__ */

Graph.cpp:

/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd.                                          *
 * @file: Graph.cpp                                                                  *
 * @author: Xu Le, He Jianfan                                                        *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#include "Graph.h"

extern int log_level;

uint32_t networkNodeNum = 0;
uint32_t linkNum = 0;
uint32_t consumptionNodeNum = 0;
uint32_t hardwareLevelNum = 0;

/**
 * @brief maintaining the map from arc ID to the point to arc, cost only O(1).
 *
 * 1. range of links that between network nodes is [0, 2*linkNum), and all integer indices are filled;
 * 2. range of links that between access network node super sink node is [2*linkNum, 2*linkNum+consumptionNodeNum);
 * 3. range of links that between super source node and server deployment nodes is 
 *    [2*linkNum+consumptionNodeNum, 2*linkNum+consumptionNodeNum+networkNodeNum);
 * 4. range of artificial dummy arcs is [2*linkNum+consumptionNodeNum+networkNodeNum, 2*linkNum+2*consumptionNodeNum+2*networkNodeNum+2).
 */
static struct Arc *arcTable;
/** maintaining the map from server hardware level to its price, cost only O(1). */
std::vector<int> priceTable;
/** maintaining the map from server hardware level to its output capacity, cost only O(1). */
std::vector<int> outcapTable;
/** maintaining the map from network node ID to its deployment fee, cost only O(1). */
std::vector<int> deployTable;
/** maintaining the map from comsumption node ID to its access network node ID, cost only O(1). */
std::vector<int> accessTable;
/** maintaining the map from comsumption node ID to its bandwidth demand, cost only O(1). */
std::vector<int> demandTable;
/** maintaining the map from access node ID to its provide comsumption node ID (reverse table of accessTable), cost only O(1). */
std::unordered_map<int /* access node */, int /* consumption node */> provideTable;

/** automatically increase ID for all arcs. */
static int arcIDIncrement = 0;

/** ownID must less than 2*linkNum. */
struct Arc* oppositeArc(int ownID)
{
    if (ownID % 2 == 0)
        return &arcTable[ownID+1]; // 0 to 1
    else
        return &arcTable[ownID-1]; // 1 to 0
}

Arc::Arc() : arcID(arcIDIncrement), srcID(-1), dstID(-1), bandwidth(0), rental(INF), flow(0), status(ArcStatus::L), nextArc(NULL)
{
    ++arcIDIncrement;
}

Node::Node() : potential(0), treeLevel(1), restrictedCap(outcapTable[hardwareLevelNum-1]), prev(NULL), next(NULL), enterArc(NULL), dummyArc(NULL), firstArc(NULL), lastArc(NULL)
{

}

/**
 * constructor of class Graph.
 */
Graph::Graph(uint32_t _node_num) : nodeNum(_node_num), arcNum(0), mcfStatus(MCFStatus::Unsolved),
    superSource(_node_num), superSink(_node_num+1), dummyRoot(_node_num+2), sinkArcs(2*linkNum),
    sourceArcs(sinkArcs+consumptionNodeNum), dummyArcs(sourceArcs+networkNodeNum), endDummyArcs(dummyArcs+_node_num+2),
    exchangeOperation(false)
{
    NodeList = new Node[nodeNum + 3]; // plus three, one for super source, one for super sink and one for dummy root
    dist = new int[nodeNum + 2];
    color = new Colortype[nodeNum + 2];
    pi = new int[nodeNum + 2];
    arcTable = new Arc[endDummyArcs]; // endDummyArcs == 2*linkNum + consumptionNodeNum + networkNodeNum + _node_num + 2
    arcRecord = new Arc*[nodeNum + 2];
    fluxThroughNode = new int[nodeNum];
}

/**
 * destructor of class Graph.
 */
Graph::~Graph()
{
    delete []fluxThroughNode;
    delete []arcRecord;
    delete []arcTable;
    delete []pi;
    delete []color;
    delete []dist;
    delete []NodeList;
}

/**
 * construct the graph in the structure of adjacency list.
 */
void Graph::construct(std::list<struct LinkTuple>& linkTuples)
{
    std::list<struct LinkTuple>::iterator iter1 = linkTuples.begin(), iter2;
    uint32_t arcID = 0;
    uint32_t curNode = 0;
    uint32_t rank = 2;
    struct Arc *firstarc = NULL, *parc = NULL, *qarc = NULL;

    firstarc = &arcTable[arcID];
    firstarc->srcID = iter1->src;
    firstarc->dstID = iter1->dst;
    firstarc->bandwidth = iter1->bandwidth;
    firstarc->rental = iter1->rental;
    firstarc->nextArc = NULL;

    curNode = iter1->src; // node index start with 0
    NodeList[curNode].firstArc = firstarc;
    NodeList[curNode].lastArc = firstarc;
    while (true)
    {
        iter2 = iter1; // iter2 is always a forword element of iter1
        ++iter2;
        if (iter2 == linkTuples.end())
            break;
        if (iter2->src == iter1->src)
        {
            if (rank % 2 == 0)
            {
                arcID += 2;
                parc = &arcTable[arcID];
                parc->srcID = iter2->src;
                parc->dstID = iter2->dst;
                parc->bandwidth = iter2->bandwidth;
                parc->rental = iter2->rental;
                parc->nextArc = NULL;

                if (rank == 2)
                    firstarc->nextArc = parc;
                else
                    qarc->nextArc = parc;
                NodeList[curNode].lastArc = parc;
            }
            else
            {
                arcID += 2;
                qarc = &arcTable[arcID];
                qarc->srcID = iter2->src;
                qarc->dstID = iter2->dst;
                qarc->bandwidth = iter2->bandwidth;
                qarc->rental = iter2->rental;
                qarc->nextArc = NULL;

                parc->nextArc = qarc;
                NodeList[curNode].lastArc = qarc;
            }
            rank++;
        }
        else
        {
            rank = 2;
            curNode = iter2->src;

            arcID += 2;
            firstarc = &arcTable[arcID];
            firstarc->srcID = curNode;
            firstarc->dstID = iter2->dst;
            firstarc->bandwidth = iter2->bandwidth;
            firstarc->rental = iter2->rental;
            firstarc->nextArc = NULL;

            NodeList[curNode].firstArc = firstarc;
            NodeList[curNode].lastArc = firstarc;
        }
        ++iter1;
    }
}

/**
 * @brief tranform directed graph to be undirected graph.
 */
void Graph::undirectify(std::list<struct LinkTuple>& linkTuples)
{
    std::list<struct LinkTuple>::iterator iter = linkTuples.begin();
    struct Arc *parc = NULL;
    
    uint32_t arcID = 1; // set arc x and x+1 be the same link with opposite direction, x is an even integer
    
    for (; iter != linkTuples.end(); ++iter, arcID += 2)
    {
        uint32_t curNode = iter->dst; // alias

        parc = &arcTable[arcID];
        parc->srcID = curNode;
        parc->dstID = iter->src;
        parc->bandwidth = iter->bandwidth;
        parc->rental = iter->rental;
        parc->nextArc = NULL;

        if (NodeList[curNode].firstArc == NULL)
            NodeList[curNode].firstArc = parc;
        else
            NodeList[curNode].lastArc->nextArc = parc; // append to the last
        NodeList[curNode].lastArc = parc; // update lastArc
    }
}

/**
 * destruct the graph and free memory.
 */
void Graph::destruct()
{
    for (uint32_t i = 0; i < nodeNum; ++i)
    {
        NodeList[i].firstArc = NULL;
        NodeList[i].lastArc = NULL;
    }
}

/**
 * display the graph whose structure is adjacency list.
 */
void Graph::display()
{
    if (log_level < DEBUG_LEVEL)
        return;

    printf("Graph's adjacency list displays: \n");
    for (uint32_t i = 0; i < nodeNum; i++)
    {
        struct Arc *darc = NULL;
        printf("node: [%u]", i);
        for (darc = NodeList[i].firstArc; darc != NULL; darc = darc->nextArc)
            printf(" -> %d(%d,%d)", darc->dstID, darc->bandwidth, darc->rental);
        printf("\n");
    }
}

/** a collection of things to be done before executing minimum cost flow algorithm, this function called only once. */
void Graph::preMinimumCostFlow()
{
    uint32_t i = 0;

    sumFlux = 0;
    for (i = 0; i < consumptionNodeNum; ++i)
        sumFlux += demandTable[i];

    candidateListNum = nodeNum > 1000 ? 50 : 30;
    hotListNum = nodeNum > 1000 ? 10 : 6;

    candidate = new Candidate[hotListNum + candidateListNum + 1];

    uint32_t arcID = dummyArcs;
    struct Arc *parc = &arcTable[arcID++];
    parc->srcID = 0;
    parc->dstID = dummyRoot;
    parc->bandwidth = INF;
    parc->rental = 10000000;
    parc->status = ArcStatus::T;
    NodeList[0].prev = &NodeList[dummyRoot];
    NodeList[0].next = &NodeList[1];
    NodeList[0].dummyArc = parc;
    NodeList[0].enterArc = parc;
    for (i = 1; i < nodeNum+1; ++i, ++arcID) // source nodes or transit node
    {
        parc = &arcTable[arcID];
        parc->srcID = static_cast<int>(i);
        parc->dstID = dummyRoot;
        parc->bandwidth = INF;
        parc->rental = 10000000;
        parc->status = ArcStatus::T;
        NodeList[i].prev = &NodeList[i-1];
        NodeList[i].next = &NodeList[i+1];
        NodeList[i].dummyArc = parc;
        NodeList[i].enterArc = parc;
    }
    NodeList[superSource].potential = 0;
    NodeList[superSource].dummyArc->flow = sumFlux;
    parc = &arcTable[arcID];
    parc->srcID = dummyRoot;
    parc->dstID = superSink;
    parc->bandwidth = INF;
    parc->rental = 10000000;
    parc->status = ArcStatus::T;
    NodeList[superSink].potential = 20000000;
    NodeList[superSink].prev = &NodeList[superSource];
    NodeList[superSink].next = NULL;
    NodeList[superSink].dummyArc = parc;
    NodeList[superSink].enterArc = parc;
    NodeList[superSink].dummyArc->flow = sumFlux;
    NodeList[dummyRoot].potential = 10000000;
    NodeList[dummyRoot].treeLevel = 0;
    NodeList[dummyRoot].prev = NULL;
    NodeList[dummyRoot].next = &NodeList[0];

    // modify arcs from super source node to server deployment nodes, these status
    // will stay constant in addSuperSource() or delSuperSource(), thus only do once here
    for (i = sourceArcs; i < dummyArcs; ++i)
    {
        arcTable[i].srcID = superSource;
        arcTable[i].bandwidth = outcapTable[hardwareLevelNum-1]; // throughput of network nodes are the highest level CDN server's output capacity
        arcTable[i].rental = 0; // access link need no rental fee
    }

    /*
    visited = new bool[nodeNum+2];
    fluxFeeArray = new FluxFee[networkNodeNum*networkNodeNum];
    feeMatrix = new FluxFee*[networkNodeNum]; // a point array, convenient to use form feeMatrix[i][j]
    for (uint32_t k = 0; k < networkNodeNum; ++k)
        feeMatrix[k] = fluxFeeArray + k*networkNodeNum;
    for (int i = 0; i < static_cast<int>(networkNodeNum); ++i)
    {
        for (int j = 0; j < static_cast<int>(networkNodeNum); ++j)
        {
            feeMatrix[i][j].from = i;
            feeMatrix[i][j].to = j;
        }
    }
    */

    _addSuperSink();
}

/** API function for Greedy. */
void Graph::setExchangeOperation(bool _exchange_operation)
{
    exchangeOperation = _exchange_operation;
}

/** API function for Greedy. */
void Graph::setOutputFluxUpbound(uint32_t nodeIdx, int upbound)
{
    NodeList[nodeIdx].restrictedCap = upbound;
}

/** Network Primal Simplex Algorithm. */
long Graph::minimumCostFlow(std::vector<int>& serversOutputFlux)
{
    long totalCost = 0;

    mcfStatus = MCFStatus::Unsolved;

    uint32_t i = 0;
    struct Arc *enteringArc = NULL, *leavingArc = NULL, *parc = NULL;

    groupNum = (arcNum-1)/candidateListNum + 1;
    groupPos = 0;
    tmpCandidateListNum = 0;

    while (mcfStatus == MCFStatus::Unsolved)
    {
        enteringArc = _executeCandidateListPivot();

        if (enteringArc != NULL)
        {
            int n1 = enteringArc->dstID, n2 = enteringArc->srcID;
            int t = 0, tau = enteringArc->status != ArcStatus::U ? enteringArc->bandwidth - enteringArc->flow : enteringArc->flow;

            if (enteringArc->status != ArcStatus::U)
                std::swap(n1, n2);

            leavingArc = NULL;
            int storeN1 = n1, storeN2 = n2;
            bool isLeave = false, isLeavingReductFlow = _reductCost(enteringArc) > 0;
            while (n1 != n2)
            {
                int oldN1 = n1, oldN2 = n2;
                if (NodeList[n1].treeLevel > NodeList[n2].treeLevel)
                {
                    parc = NodeList[n1].enterArc;
                    t = parc->srcID == n1 ? parc->flow : parc->bandwidth - parc->flow;
                    isLeave = parc->srcID == n1;

                    n1 = _parent(n1, parc);
                }
                else
                {
                    parc = NodeList[n2].enterArc;
                    t = parc->srcID != n2 ? parc->flow : parc->bandwidth - parc->flow;
                    isLeave = parc->srcID != n2;

                    n2 = _parent(n2, parc);
                }
                if (t < tau || (t <= tau && NodeList[oldN1].treeLevel <= NodeList[oldN2].treeLevel))
                {
                    tau = t;
                    leavingArc = parc;
                    isLeavingReductFlow = isLeave;
                }
            }

            if (leavingArc == NULL)
                leavingArc = enteringArc;

            if (tau >= INF)
            {
                mcfStatus = MCFStatus::Unbounded;
                break;
            }

            n1 = storeN1;
            n2 = storeN2;
            if (tau != 0)
            {
                enteringArc->flow += (enteringArc->srcID != n1) ? -tau : tau;

                while (n1 != n2)
                {
                    if (NodeList[n1].treeLevel > NodeList[n2].treeLevel)
                    {
                        parc = NodeList[n1].enterArc;
                        parc->flow += (parc->srcID == n1) ? -tau : tau;

                        n1 = _parent(n1, NodeList[n1].enterArc);
                    }
                    else
                    {
                        parc = NodeList[n2].enterArc;
                        parc->flow += (parc->srcID != n2) ? -tau : tau;

                        n2 = _parent(n2, NodeList[n2].enterArc);
                    }
                }
            }

            if (enteringArc != leavingArc)
            {
                bool isLeavingBringFlow = isLeavingReductFlow == (NodeList[leavingArc->srcID].treeLevel > NodeList[leavingArc->dstID].treeLevel);
                bool isEnteringEqualsN1 = enteringArc->srcID == storeN1;

                n1 = enteringArc->dstID;
                n2 = enteringArc->srcID;

                if (isEnteringEqualsN1 != isLeavingBringFlow)
                    std::swap(n1, n2);
            }

            leavingArc->status = isLeavingReductFlow ? ArcStatus::L : ArcStatus::U;

            if (leavingArc != enteringArc)
            {
                enteringArc->status = ArcStatus::T;

                if (NodeList[leavingArc->srcID].treeLevel < NodeList[leavingArc->dstID].treeLevel)
                    _updateTree(enteringArc, leavingArc->dstID, n1, n2);
                else
                    _updateTree(enteringArc, leavingArc->srcID, n1, n2);

                n2 = enteringArc->dstID;
                int delta = _reductCost(enteringArc);
                if (NodeList[enteringArc->srcID].treeLevel > NodeList[enteringArc->dstID].treeLevel)
                {
                    delta = -delta;
                    n2 = enteringArc->srcID;
                }

                // execute add potential operation
                int level = NodeList[n2].treeLevel;
                struct Node *pnode = &NodeList[n2];

                do {
                    pnode->potential += delta;
                    pnode = pnode->next;
                }
                while (pnode != NULL && pnode->treeLevel > level);
            }
        }
        else
        {
            mcfStatus = MCFStatus::Solved;
            for (i = dummyArcs; i < endDummyArcs; ++i)
                if (arcTable[i].flow > 0)
                    mcfStatus = MCFStatus::Unfeasible;
        }
    }

    if (mcfStatus == MCFStatus::Solved)
    {
        for (i = sourceArcs; i < arcNum; ++i)
            serversOutputFlux[i-sourceArcs] = arcTable[i].flow; // adapt overlaying method other than clear() and push_back() method for efficiency
        
        if (exchangeOperation)
        {
            memset(fluxThroughNode, 0, networkNodeNum*sizeof(int));
            for (i = 0; i < networkNodeNum; ++i)
                for (parc = NodeList[i].firstArc; parc != NULL; parc = parc->nextArc)
                    fluxThroughNode[i] += parc->flow;
        }
    }

    if (mcfStatus == MCFStatus::Solved)
    {
        for (i = 0; i < sourceArcs; ++i)
            if (arcTable[i].status == ArcStatus::T || arcTable[i].status == ArcStatus::U)
                totalCost += arcTable[i].flow * arcTable[i].rental;
    }
    else
        totalCost = -1;

    return totalCost;
}

struct Arc* Graph::_executeCandidateListPivot()
{
    uint32_t i = 0, next = 0, minimeValue = (hotListNum < tmpCandidateListNum) ? hotListNum : tmpCandidateListNum;
    int reductC = 0;
    struct Arc *parc = NULL;

    for (i = 2; i <= minimeValue; ++i)
    {
        parc = candidate[i].arc;
        reductC = _reductCost(parc);

        if ((reductC < 0 && parc->status == ArcStatus::L) || (reductC > 0 && parc->status == ArcStatus::U))
        {
            ++next;
            candidate[next].arc = parc;
            candidate[next].absReductC = (reductC >= 0) ? reductC : -reductC;
        }
    }

    tmpCandidateListNum = next;
    uint32_t oldGroupPos = groupPos;
    do {
        for (i = groupPos; i < arcNum; i += groupNum)
        {
            parc = &arcTable[i];

            if (parc->status == ArcStatus::L)
            {
                reductC = _reductCost(parc);
                if (reductC < 0)
                {
                    ++tmpCandidateListNum;
                    candidate[tmpCandidateListNum].arc = parc;
                    candidate[tmpCandidateListNum].absReductC = (reductC >= 0) ? reductC : -reductC;
                }
            }
            else if (parc->status == ArcStatus::U)
            {
                reductC = _reductCost(parc);
                if (reductC > 0)
                {
                    ++tmpCandidateListNum;
                    candidate[tmpCandidateListNum].arc = parc;
                    candidate[tmpCandidateListNum].absReductC = (reductC >= 0) ? reductC : -reductC;
                }
            }
        }

        if (++groupPos >= groupNum)
            groupPos = 0;
    }
    while (tmpCandidateListNum < hotListNum && groupPos != oldGroupPos);

    if (tmpCandidateListNum > 0)
        _sortCandidateList(1, tmpCandidateListNum);
    
    return tmpCandidateListNum > 0 ? candidate[1].arc : NULL;
}

void Graph::_sortCandidateList(const uint32_t min , const uint32_t max)
{
    uint32_t low = min, high = max;

    int cut = candidate[(low + high)/2].absReductC; // alias
    do {
        while (candidate[low].absReductC > cut) 
            ++low;
        while (candidate[high].absReductC < cut) 
            --high;

        if (low < high)
            std::swap(candidate[low], candidate[high]);

        if (low <= high)
        {
            ++low;
            --high;
        }
    }
    while ( low <= high );

    if (min < high) 
        _sortCandidateList(min, high);
    if (low < max && low <= hotListNum) 
        _sortCandidateList(low, max);
}

int Graph::_reductCost(struct Arc *parc)
{
    return parc->rental + NodeList[parc->srcID].potential - NodeList[parc->dstID].potential;
}

int Graph::_parent(int index, struct Arc *parc)
{
    if (parc == NULL)
    {
        error_log("parc is NULL when _parent() is called.\n");
        return -1;
    }

    return (parc->srcID == index) ? parc->dstID : parc->srcID;
}

void Graph::_updateTree(struct Arc *enteringArc, const int n, const int n1, const int n2)
{
    int delta = NodeList[n1].treeLevel + 1 - NodeList[n2].treeLevel;
    int root = n2, parent = 0;

    struct Node *prevNode = &NodeList[n1], *lastNode = NULL;
    struct Arc *parc = enteringArc, *qarc = NULL;

    bool ok = false;
    while (ok == false)
    {
        ok = root == n;

        parent = _parent(root, NodeList[root].enterArc);
        lastNode = _cutAndUpdateSubtree(&NodeList[root], delta);
        delta += 2;
        _pasteSubtree(&NodeList[root], lastNode, prevNode);
        prevNode = lastNode;

        qarc = NodeList[root].enterArc;
        NodeList[root].enterArc = parc;
        parc = qarc;
        root = parent;
    }
}

struct Node* Graph::_cutAndUpdateSubtree(struct Node *root, const int delta)
{
    int level = root->treeLevel;
    struct Node *pnode = root;
    while (pnode->next && pnode->next->treeLevel > level)
    {
        pnode = pnode->next;
        pnode->treeLevel += delta;
    }

    root->treeLevel += delta;

    if (root->prev != NULL)
        root->prev->next = pnode->next;
    if (pnode->next != NULL)
        pnode->next->prev = root->prev;

    return pnode;
}

void Graph::_pasteSubtree(struct Node *root, struct Node *lastNode, struct Node *prevNode)
{
    struct Node *nextNode = prevNode->next;
    root->prev = prevNode;
    prevNode->next = root;
    lastNode->next = nextNode;
    if (nextNode != NULL)
        nextNode->prev = lastNode;
}

/** a collection of things to be done after executing minimum cost flow algorithm, this function called only once. */
void Graph::postMinimumCostFlow()
{
    _delSuperSink();

    delete []candidate;

    /*
    delete []visited;
    delete []feeMatrix;
    delete []fluxFeeArray;
    */
}

/** used to record flow paths after minimum cost maximum flow algorithm finished, this function should be called only once. */
void Graph::recordFlowPath()
{
    int source = superSource;
    int sink = superSink;
    std::vector<int> path;
    path.reserve(16);
    while ( _DFS(source, sink, path, INF) )
        ;
}

/** called by recordFlowPath(). */
bool Graph::_DFS(int s, int t, std::vector<int>& path, int tmpFlow)
{
    if (s == t)
    {
        path.push_back(tmpFlow);
        fluxPathList.push_back(path);
        path.clear();
        minFlow = tmpFlow;
        return true;
    }
    struct Arc *parc = NodeList[s].firstArc;
    while (parc != NULL)
    {
        if (parc->flow > 0)
        {
            if (parc->dstID != t)
                path.push_back(parc->dstID);
            if ( _DFS(parc->dstID, t, path, std::min(tmpFlow, parc->flow)) )
            {
                parc->flow -= minFlow;
                return true;
            }
        }
        parc = parc->nextArc;
    }
    return false;
}

/** add the super source node, this function should be called every time right before algorithm starts. */
void Graph::addSuperSource(std::list<int>& servers)
{
    struct Arc *parc = NULL;

    sourceNum = static_cast<uint32_t>(servers.size());
    arcNum = sourceArcs + sourceNum;

    uint32_t arcID = sourceArcs;

    for (std::list<int>::iterator iter = servers.begin(); iter != servers.end(); ++iter, ++arcID)
    {
        parc = &arcTable[arcID];
        parc->dstID = *iter; // network node which deploys a CDN server
        parc->bandwidth = NodeList[parc->dstID].restrictedCap;
        parc->nextArc = NULL;

        if (NodeList[superSource].firstArc == NULL)
            NodeList[superSource].firstArc = parc;
        else
            NodeList[superSource].lastArc->nextArc = parc; // append to the last
        NodeList[superSource].lastArc = parc; // update lastArc too
    }
}

/** delete the super source node, this function should be called every time right after minimumCostFlow() is called. */
void Graph::delSuperSource()
{
    _revertStatusToInitialization();

    NodeList[superSource].firstArc = NULL;
    NodeList[superSource].lastArc = NULL;

    for (uint32_t i = sourceArcs; i < sourceArcs+sourceNum; ++i)
        arcTable[i].dstID = -1;
}

/** display arcs from super source to deployment node, only useful when debugging. */
void Graph::displaySuperSource()
{
    if (log_level < DEBUG_LEVEL)
        return;

    printf("Super source node's adjacency list displays: \n");

    struct Arc *darc = NULL;
    printf("node: [%d]", superSource);
    for (darc = NodeList[superSource].firstArc; darc != NULL; darc = darc->nextArc)
        printf(" -> %d(%d,%d)", darc->dstID, darc->bandwidth, darc->rental);
    printf("\n");
}

/** revert the status of all arcs to initialization, this function automatically called by delSuperSource(). */
void Graph::_revertStatusToInitialization()
{
    uint32_t i;
    // revert arcs' status
    for (i = 0; i < arcNum; ++i)
    {
        arcTable[i].flow = 0;
        arcTable[i].status = ArcStatus::L;
    }
    for (i = dummyArcs; i < endDummyArcs; ++i)
    {
        arcTable[i].flow = 0;
        arcTable[i].status = ArcStatus::T;
    }
    arcTable[endDummyArcs-2].flow = sumFlux;
    arcTable[endDummyArcs-1].flow = sumFlux;

    // revert nodes' status
    NodeList[0].potential = 0;
    NodeList[0].treeLevel = 1;
    NodeList[0].prev = &NodeList[dummyRoot];
    NodeList[0].next = &NodeList[1];
    NodeList[0].enterArc = NodeList[0].dummyArc;
    for (i = 1; i < nodeNum+1; ++i) // source nodes or transit node
    {
        NodeList[i].potential = 0;
        NodeList[i].treeLevel = 1;
        NodeList[i].prev = &NodeList[i-1];
        NodeList[i].next = &NodeList[i+1];
        NodeList[i].enterArc = NodeList[i].dummyArc;
    }
    NodeList[superSink].potential = 20000000;
    NodeList[superSink].treeLevel = 1;
    NodeList[superSink].prev = &NodeList[superSource];
    NodeList[superSink].next = NULL;
    NodeList[superSink].enterArc = NodeList[superSink].dummyArc;
    NodeList[dummyRoot].potential = 10000000;
    NodeList[dummyRoot].treeLevel = 0;
    NodeList[dummyRoot].prev = NULL;
    NodeList[dummyRoot].next = &NodeList[0];

    // revert candidates' status
    for (i = 0; i <= hotListNum + candidateListNum; ++i)
    {
        candidate[i].absReductC = 0;
        candidate[i].arc = NULL;
    }
}

/** add the super sink node, this function is called by preMinimumCostFlow(). */
void Graph::_addSuperSink()
{
    struct Arc *parc = NULL;

    uint32_t arcID = sinkArcs;

    for (uint32_t i = 0; i < consumptionNodeNum; ++i, ++arcID)
    {
        int curNode = accessTable[i]; // access node index

        parc = &arcTable[arcID];
        parc->srcID = curNode;
        parc->dstID = superSink;          // super sink node
        parc->bandwidth = demandTable[i]; // demand bandwidth of each consumption node
        parc->rental = 0;                 // access link need no rental fee
        parc->nextArc = NULL;

        if (NodeList[curNode].firstArc == NULL)
            NodeList[curNode].firstArc = parc;
        else
            NodeList[curNode].lastArc->nextArc = parc; // append to the last
        NodeList[curNode].lastArc = parc;  // update lastArc too
    }
}

/** delete the super sink node, this function is called by postMinimumCostFlow(). */
void Graph::_delSuperSink()
{
    uint32_t i = 0;
    for (i = 0; i < consumptionNodeNum; ++i)
    {
        int curNode = accessTable[i];

        struct Arc *lastarc = NodeList[curNode].firstArc, *prevarc = NULL;
        while (lastarc->nextArc != NULL)
        {
            prevarc = lastarc;
            lastarc = lastarc->nextArc;
        }

        if (NodeList[curNode].firstArc->nextArc == NULL)
        {
            NodeList[curNode].firstArc = NULL;
            NodeList[curNode].lastArc = NULL;
        }
        else
        {
            prevarc->nextArc = NULL;
            NodeList[curNode].lastArc = prevarc;
        }
    }

    for (i = sinkArcs; i < sourceArcs; ++i)
        arcTable[i].srcID = -1;
}

/**
 * use BFS algorithm to traverse the graph at the start node src.
 */
void Graph::BFS(int src)
{
    int u, v;
    for (uint32_t i = 0; i < nodeNum; ++i)
    {
        color[i] = Colortype::White;
        dist[i] = INF;
        pi[i] = -1;
    }
    color[src] = Colortype::Gray;
    dist[src] = 0;
    pi[src] = -1;
    arcRecord[src] = NULL;

    std::queue<int> Q;
    Q.push(src);
    while (!Q.empty())
    {
        u = Q.front();
        Q.pop();
        struct Arc *parc = NodeList[u].firstArc;
        while (parc != NULL)
        {
            v = parc->dstID;
            if (color[v] == Colortype::White && parc->rental != INF)
            {
                color[v] = Colortype::Gray;
                dist[v] = dist[u] + 1;
                pi[v] = u;
                arcRecord[v] = parc;
                Q.push(v);
            }
            parc = parc->nextArc;
        }
        color[u] = Colortype::Black;
    }
}

/**
 * use dijkstra algorithm to calculate the minimum distance from node src to other nodes.
 */
void Graph::dijkstra(int src)
{
    int u, v;
    for (uint32_t i = 0; i < nodeNum; ++i)
    {
        dist[i] = INF;
        pi[i] = 0;
    }
    dist[src] = 0;
    pi[src] = -1;
    arcRecord[src] = NULL;

    std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int> >, std::greater<std::pair<int, int> > > priorityQ;
    priorityQ.push(std::pair<int, int>(0, src));
    while (!priorityQ.empty())
    {
        u = priorityQ.top().second;
        priorityQ.pop();
        struct Arc *parc = NodeList[u].firstArc;
        while (parc != NULL)
        {
            v = parc->dstID;
            if (dist[v] > dist[u] + parc->rental)
            {
                dist[v] = dist[u] + parc->rental;
                pi[v] = u;
                arcRecord[v] = parc;
                priorityQ.push(std::pair<int, int>(dist[v], v));
            }
            parc = parc->nextArc;
        }
    }
}

/**
 * obtain the node path from src to dst after call BFS() or dijkstra().
 */
void Graph::recordNodePath(std::vector<int>& path, int src, int dst)
{
    std::stack<int> S;
    while (dst != src)
    {
        S.push(dst);
        dst = pi[dst];
    }
    S.push(dst);
    while (!S.empty())
    {
        path.push_back(S.top());
        S.pop();
    }
}

/**
 * obtain the arc path from src to dst after call BFS() or dijkstra().
 */
long Graph::recordArcPath(std::vector<struct Arc*>& path, int src, int dst)
{
    std::stack<int> S;
    long costSum = 0;
    while (dst != src)
    {
        int tmp = dst;
        dst = arcRecord[dst]->arcID;
        S.push(dst);
        dst = pi[tmp];
    }
    while (!S.empty())
    {
        path.push_back(&arcTable[S.top()]);
        costSum += arcTable[S.top()].rental;
        S.pop();
    }
    return costSum;
}

/** verification of the final CDN server deployment scheme. */
bool verifyScheme(Graph& graph, long& totalRentalFee, long& totalPurchaseFee)
{
    if (graph.fluxPathList.size() > 300000)
    {
        error_log("The number of flux paths has overstep the limits!\n");
        return false;
    }

    uint32_t i = 0;
    totalRentalFee = 0;
    totalPurchaseFee = 0;
    std::vector<int> arcLeftBandwidth(graph.arcNum, 0);
    std::vector<int> demandSatified(consumptionNodeNum, 0);
    std::unordered_set<int> serverNodeSet;
    for (i = 0; i < graph.arcNum; ++i)
        arcLeftBandwidth[i] = arcTable[i].bandwidth;

    int fluxPathIdx = 1;
    for (std::list<std::vector<int> >::iterator iter = graph.fluxPathList.begin(); iter != graph.fluxPathList.end(); ++iter, ++fluxPathIdx)
    {
        debug_log("verify flux path %d...\n", fluxPathIdx);
        
        std::vector<int>& fluxPath = *iter; // alias
        if (fluxPath.size() < 4)
        {
            error_log("The number of nodes in a flux path less than 3, it must be an invalid flux path!\n");
            return false;
        }
        if (fluxPath.size() > 10000)
        {
            error_log("The number of nodes in a flux path has overstep the limits!\n");
            return false;
        }

        int serverNodeID = fluxPath[0];
        uint32_t accessNodeIdx = static_cast<uint32_t>(fluxPath.size()) - 4;
        int consumptionNodeID = fluxPath[accessNodeIdx+1];
        int consumptionBandwidth = fluxPath[accessNodeIdx+2];
        int serverHardwareLevel = fluxPath.back();
        if (fluxPath[accessNodeIdx] != accessTable[consumptionNodeID])
        {
            error_log("The access network node[%d] in the flux path is not the same with that required[%d]!\n", fluxPath[accessNodeIdx], accessTable[consumptionNodeID]);
            return false;
        }
        demandSatified[consumptionNodeID] += consumptionBandwidth;

        if (serverNodeSet.find(serverNodeID) == serverNodeSet.end())
        {
            serverNodeSet.insert(serverNodeID);
            totalPurchaseFee += priceTable[serverHardwareLevel];
        }

        struct Arc *parc = graph.NodeList[serverNodeID].firstArc;
        for (i = 1; i <= accessNodeIdx; ++i)
        {
            while (parc != NULL)
            {
                if (parc->dstID == fluxPath[i])
                {
                    totalRentalFee += parc->rental*consumptionBandwidth;
                    arcLeftBandwidth[parc->arcID] -= consumptionBandwidth;
                    if (arcLeftBandwidth[parc->arcID] < 0)
                    {
                        error_log("The left bandwidth of arc[%d] which is %d -> %d belows zero!\n", parc->arcID, parc->srcID, parc->dstID);
                        return false;
                    }
                    parc = graph.NodeList[parc->dstID].firstArc;
                    break;
                }
                parc = parc->nextArc;
            }
            if (parc == NULL)
            {
                error_log("The flux path has an arc that is not in the graph!\n");
                return false;
            }
        }
    }
    
    for (uint32_t k = 0; k < consumptionNodeNum; ++k)
    {
        if (demandSatified[k] < demandTable[k])
        {
            error_log("The flux scheme cannot satify the bandwidth requirement of consumption node[%u]!\n", k);
            return false;
        }
    }
    
    return true;
}

TopologyScanner.h:

/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd.                                          *
 * @file: TopologyScanner.h                                                          *
 * @author: Xu Le                                                                    *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#ifndef __TOPOLOGYSCANNER_H__
#define __TOPOLOGYSCANNER_H__

#include "Graph.h"

class Greedy; // forward declaration

/** A class undertaking to scan graph topology to find some useful results. */
class TopologyScanner
{
public:
    explicit TopologyScanner(Graph& _graph);
    ~TopologyScanner();

    TopologyScanner(const TopologyScanner& rhs) = delete;
    TopologyScanner& operator=(const TopologyScanner& rhs) = delete;

    void scan();
    void recommend();

    void resetRestrictedCap();

private:
    /** description of the density of arcs in graph. */
    enum class ArcDensity {
        Unknown = 0,
        UltraSparse,
        VerySparse,
        Sparse,
        General,
        Dense,
        VeryDense,
        UltraDense
    };
    /** description of the density of consumption nodes in graph. */
    enum class CspDensity {
        Unknown = 8,
        UltraSparse,
        VerySparse,
        Sparse,
        General,
        Dense,
        VeryDense,
        UltraDense
    };
    const char* _arcDensityString();
    const char* _cspDensityString();
    void _defineDensity();
    void _mustDeployOnAccess();
    void _decideMultiplicativeFactor();
    void _selectByScore();

private:
    friend class Greedy;

    Graph &graph; ///< reference to graph.
    ArcDensity arcDensity; ///< stores the density of arcs in graph.
    CspDensity cspDensity; ///< stores the density of consumption nodes in graph.
    int mNetworkNodeNum;   ///< equals to networkNodeNum, just get rid of annoying type incompatible warning.

    /** @name status of all network nodes. */
    ///@{
    int *outLinkCap;       ///< the sum up capacity of all links of a network node.
    int *nodeLevel;        ///< the out link capacity level of a network node.
    double *outLinkFee;    ///< the weighted average rental fee of all links of a network node.
    double *capRank;       ///< the sum up capacity rank of a network node.
    double *rentalFeeRank; ///< the weighted average rental fee rank of a network node.
    double *deployFeeRank; ///< the deployment fee rank of a network node.
    int *scoreTable;       ///< stores score of all network nodes.
    double capVariance;    ///< variance of node output capacity
    ///@}

    double multiplicativeFactor;  ///< the result multiplied by consumption node num is the number of network nodes to be selected as possible servers' deployment location. (decided by cspDensity)
    std::vector<int> possibleServers; ///< all network nodes with high score that are possibly chosen as where to deploy servers.
    std::set<int> definiteServers;    ///< set of access nodes that have to deploy a cdn server.
};

#endif /* __TOPOLOGYSCANNER_H__ */

TopologyScanner.cpp:

/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd.                                          *
 * @file: TopologyScanner.cpp                                                        *
 * @author: Xu Le                                                                    *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#include "TopologyScanner.h"

int maxUnitRentalFee = 0;

extern int log_level;

extern uint32_t networkNodeNum;
extern uint32_t linkNum;
extern uint32_t consumptionNodeNum;
extern uint32_t hardwareLevelNum;

extern std::vector<int> priceTable;
extern std::vector<int> outcapTable;
extern std::vector<int> deployTable;
extern std::vector<int> accessTable;
extern std::vector<int> demandTable;
extern std::unordered_map<int, int> provideTable;

TopologyScanner::TopologyScanner(Graph& _graph) : graph(_graph), arcDensity(ArcDensity::Unknown), cspDensity(CspDensity::Unknown), multiplicativeFactor(2.0)
{
    mNetworkNodeNum = static_cast<int>(networkNodeNum);

    outLinkCap = new int[networkNodeNum];
    outLinkFee = new double[networkNodeNum];
    nodeLevel = new int[networkNodeNum];
    capRank = new double[networkNodeNum];
    rentalFeeRank = new double[networkNodeNum];
    deployFeeRank = new double[networkNodeNum];
    scoreTable = new int[networkNodeNum];
    std::fill(outLinkCap, outLinkCap + networkNodeNum, 0);
    std::fill(outLinkFee, outLinkFee + networkNodeNum, 0.0);
}

TopologyScanner::~TopologyScanner()
{
    delete []outLinkCap;
    delete []outLinkFee;
    delete []nodeLevel;
    delete []capRank;
    delete []rentalFeeRank;
    delete []deployFeeRank;
    delete []scoreTable;
}

/** scan the network topology of graph. */
void TopologyScanner::scan()
{
    uncond_log("\n====================    Enter topology scanner category    ====================\n");

    _defineDensity();

    uint32_t i = 0;
    double capAverage = 0.0;
    double capSquareSum = 0.0;

    for (i = 0; i < networkNodeNum; ++i)
    {
        struct Arc *parc = graph.NodeList[i].firstArc;
        while (parc != NULL)
        {
            // if (parc->dstID < static_cast<int>(networkNodeNum))
            // {
            outLinkCap[i] += parc->bandwidth;
            outLinkFee[i] += parc->bandwidth*parc->rental;
            // }
            parc = parc->nextArc;
        }
    }
    std::vector<std::pair<int, int> > sortLinkCap;
    std::vector<std::pair<double, int> > sortLinkFee;
    std::vector<std::pair<int, int> > sortDeployFee;
    sortLinkCap.reserve(networkNodeNum);
    sortLinkFee.reserve(networkNodeNum);
    sortDeployFee.reserve(networkNodeNum);
    for (i = 0; i < networkNodeNum; ++i)
    {
        capAverage += outLinkCap[i];
        capSquareSum += square(outLinkCap[i]);
        outLinkFee[i] /= outLinkCap[i];
        nodeLevel[i] = locateLevel(outLinkCap[i]);
        if (nodeLevel[i] < static_cast<int>(hardwareLevelNum-1) && outLinkCap[i] - outcapTable[nodeLevel[i]] < 5)
            graph.NodeList[i].restrictedCap = outcapTable[nodeLevel[i]];
        sortLinkCap.emplace_back(outLinkCap[i], i);
        sortLinkFee.emplace_back(outLinkFee[i], i);
        sortDeployFee.emplace_back(deployTable[i], i);
    }
    capAverage /= networkNodeNum;
    capVariance = (capSquareSum - networkNodeNum * square(capAverage)) / networkNodeNum;
    info_log("capAverage: %f, capVariance: %f\n", capAverage, capVariance);
    std::sort(sortLinkCap.begin(), sortLinkCap.end(), std::greater<std::pair<int, int> >());
    std::sort(sortLinkFee.begin(), sortLinkFee.end());
    std::sort(sortDeployFee.begin(), sortDeployFee.end());
    for (i = 0; i < networkNodeNum; ++i)
    {
        capRank[sortLinkCap[i].second] = 1.0 - static_cast<double>(i)/mNetworkNodeNum;
        rentalFeeRank[sortLinkFee[i].second] = 1.0 - static_cast<double>(i)/mNetworkNodeNum;
    }
    uint32_t aheadRank = 0;
    deployFeeRank[sortDeployFee[0].second] = 1.0;
    for (i = 1; i < networkNodeNum; ++i)
    {
        if (sortDeployFee[i].first > sortDeployFee[i-1].first)
            aheadRank = i;
        deployFeeRank[sortDeployFee[i].second] = 1.0 - static_cast<double>(aheadRank)/mNetworkNodeNum;
    }
    for (i = 0; i < networkNodeNum; ++i)
    {
        debug_log("node[%u] -> outLinkCap: %d, outLinkFee: %f, capRank: %f, rentalFeeRank: %f\n", i, outLinkCap[i], outLinkFee[i], capRank[i], rentalFeeRank[i]);
    }

    for (i = 0; i < networkNodeNum; ++i)
        scoreTable[i] = 200 * capRank[i] + 100 * rentalFeeRank[i] + 75 * deployFeeRank[i];

    _mustDeployOnAccess();
}

/** recommend some network nodes that may be good as the input of Greedy algorithm. */
void TopologyScanner::recommend()
{
    _selectByScore();

    uint32_t i = 0, definiteNum = 0;
    // std::vector<int> certainGroup;
    debug_log("definite servers:");
    for (size_t k = 0; k < possibleServers.size(); ++k)
    {
        if (scoreTable[possibleServers[k]] != INF)
            break;
        ++definiteNum;
        definiteServers.insert(possibleServers[k]);
        if (log_level >= DEBUG_LEVEL)
            printf(" %d", possibleServers[k]);
    }
    for (i = definiteNum; i < static_cast<uint32_t>(possibleServers.size()); ++i) // shift to overlaying definite servers
        possibleServers[i-definiteNum] = possibleServers[i];
    for (i = 0; i < definiteNum; ++i)
        possibleServers.pop_back();

    if (log_level >= DEBUG_LEVEL)
        printf("\n");
    debug_log("possible servers:");
    if (log_level >= DEBUG_LEVEL) // print possible servers
    {
        for (i = 0; i < static_cast<uint32_t>(possibleServers.size()); ++i)
            printf(" %d", possibleServers[i]);
        printf("\n");
    }

    uncond_log("====================    Exit topology scanner category    ====================\n\n");
}

void TopologyScanner::resetRestrictedCap()
{
    for (uint32_t i = 0; i < networkNodeNum; ++i)
    {
        graph.NodeList[i].restrictedCap = outcapTable[hardwareLevelNum-1];
        nodeLevel[i] = locateLevel(outLinkCap[i]);
        if (nodeLevel[i] < static_cast<int>(hardwareLevelNum-1) && outLinkCap[i] - outcapTable[nodeLevel[i]] < 5)
            graph.NodeList[i].restrictedCap = outcapTable[nodeLevel[i]];
    }
}

const char* TopologyScanner::_arcDensityString()
{
    switch (arcDensity)
    {
    case ArcDensity::UltraSparse:
        return "ultra-sparse";
    case ArcDensity::VerySparse:
        return "very-sparse";
    case ArcDensity::Sparse:
        return "sparse";
    case ArcDensity::General:
        return "general";
    case ArcDensity::Dense:
        return "dense";
    case ArcDensity::VeryDense:
        return "very-dense";
    case ArcDensity::UltraDense:
        return "ultra-dense";
    default:
        return "unknown";
    }
}

const char* TopologyScanner::_cspDensityString()
{
    switch (cspDensity)
    {
    case CspDensity::UltraSparse:
        return "ultra-sparse";
    case CspDensity::VerySparse:
        return "very-sparse";
    case CspDensity::Sparse:
        return "sparse";
    case CspDensity::General:
        return "general";
    case CspDensity::Dense:
        return "dense";
    case CspDensity::VeryDense:
        return "very-dense";
    case CspDensity::UltraDense:
        return "ultra-dense";
    default:
        return "unknown";
    }
}

void TopologyScanner::_defineDensity()
{
    double _arc_density = static_cast<double>(2*linkNum) / mNetworkNodeNum;
    if (_arc_density < 2.0)
        arcDensity = ArcDensity::UltraSparse;
    else if (_arc_density >= 2.0 && _arc_density < 3.0)
        arcDensity = ArcDensity::VerySparse;
    else if (_arc_density >= 3.0 && _arc_density < 4.0)
        arcDensity = ArcDensity::Sparse;
    else if (_arc_density >= 4.0 && _arc_density < 6.0)
        arcDensity = ArcDensity::General;
    else if (_arc_density >= 6.0 && _arc_density < 9.0)
        arcDensity = ArcDensity::Dense;
    else if (_arc_density >= 9.0 && _arc_density < 14.0)
        arcDensity = ArcDensity::VeryDense;
    else // (_arc_density >= 14.0)
        arcDensity = ArcDensity::UltraDense;
    info_log("arc ratio: %f, its density level: %s\n", _arc_density, _arcDensityString());
        
    double _csp_density = static_cast<double>(consumptionNodeNum) / mNetworkNodeNum;
    if (_csp_density < 0.1)
        cspDensity = CspDensity::UltraSparse;
    else if (_csp_density >= 0.1 && _csp_density < 0.15)
        cspDensity = CspDensity::VerySparse;
    else if (_csp_density >= 0.15 && _csp_density < 0.2)
        cspDensity = CspDensity::Sparse;
    else if (_csp_density >= 0.2 && _csp_density < 0.3)
        cspDensity = CspDensity::General;
    else if (_csp_density >= 0.3 && _csp_density < 0.4)
        cspDensity = CspDensity::Dense;
    else if (_csp_density >= 0.4 && _csp_density < 0.5)
        cspDensity = CspDensity::VeryDense;
    else // (_csp_density >= 0.5)
        cspDensity = CspDensity::UltraDense;
    info_log("csp ratio: %f, its density level: %s\n", _csp_density, _cspDensityString());
}

void TopologyScanner::_mustDeployOnAccess()
{
    std::vector<std::pair<int /* rental */, int /* bandwidth */> > feeCapPair;
    feeCapPair.reserve(64);

    for (uint32_t i = 0; i < consumptionNodeNum; ++i)
    {
        int accessNode = accessTable[i]; // alias
        int demandFlux = demandTable[i]; // alias
        if (outLinkCap[accessNode] < demandFlux)
        {
            scoreTable[accessNode] = INF; // large enough, indicating this network node must be deployed with a server
            continue;
        }

        struct Arc *parc = graph.NodeList[accessNode].firstArc;
        while (parc != NULL)
        {
            if (parc->dstID < mNetworkNodeNum)
                feeCapPair.emplace_back(parc->rental, parc->bandwidth);
            parc = parc->nextArc;
        }

        std::sort(feeCapPair.begin(), feeCapPair.end(), [](const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) {
            if (lhs.first == rhs.first)
                return lhs.second > rhs.second;
            return lhs.first < rhs.first;
        });

        int inputFlux = 0, inputRentalFee = 0;
        size_t j = 0;
        for (; inputFlux < demandFlux && j < feeCapPair.size(); ++j)
        {
            inputFlux += feeCapPair[j].second;
            inputRentalFee += feeCapPair[j].first * feeCapPair[j].second;
        }
        inputRentalFee -= (inputFlux - demandFlux) * feeCapPair[j-1].first;

        debug_log("minimum input rental fee of access node [%d] is %d\n", accessNode, inputRentalFee);

        if (inputRentalFee >= deployTable[accessNode] + priceTable[0])
        {
            info_log("access node [%d] have to deploy a server.\n", accessNode);
            scoreTable[accessNode] = INF; // large enough, indicating this network node must be deployed with a server
        }

        feeCapPair.clear();
    }
}

void TopologyScanner::_decideMultiplicativeFactor()
{
    switch (cspDensity)
    {
    case CspDensity::UltraSparse:
        multiplicativeFactor = 5.0; break; // 5.0
    case CspDensity::VerySparse:
        multiplicativeFactor = 3.6; break; // 3.6
    case CspDensity::Sparse:
        multiplicativeFactor = 2.8; break; // 2.8
    case CspDensity::General:
        multiplicativeFactor = 2.0; break; // 2.0
    case CspDensity::Dense:
        multiplicativeFactor = 1.5; break; // 1.5
    case CspDensity::VeryDense:
        multiplicativeFactor = 1.2; break; // 1.2
    case CspDensity::UltraDense:
        multiplicativeFactor = 1.1; break; // 1.1
    default:
        multiplicativeFactor = 2.0; // the same as general case
    }
}

void TopologyScanner::_selectByScore()
{
    _decideMultiplicativeFactor();

    std::vector<std::pair<int /* score */, int /* ID */> > scoreIDPair;
    scoreIDPair.reserve(networkNodeNum);

    uint32_t i = 0, nonAccessNum = 0, candidateNum = static_cast<uint32_t>(multiplicativeFactor*consumptionNodeNum);
    for (i = 0; i < networkNodeNum; ++i)
        scoreIDPair.emplace_back(scoreTable[i], i);

    std::sort(scoreIDPair.begin(), scoreIDPair.end(), std::greater<std::pair<int, int> >());

    debug_log("score sort from high to low: ");
    possibleServers.reserve(candidateNum);
    for (i = 0; i < networkNodeNum; ++i)
    {
        int nodeID = scoreIDPair[i].second; // alias
        if (provideTable.find(nodeID) != provideTable.end())
        {
            if (log_level >= DEBUG_LEVEL)
                printf("%d ", scoreIDPair[i].first);
            possibleServers.push_back(nodeID);
        }
        else if (provideTable.find(nodeID) == provideTable.end() && nonAccessNum < candidateNum - consumptionNodeNum)
        {
            if (log_level >= DEBUG_LEVEL)
                printf("%d ", scoreIDPair[i].first);
            possibleServers.push_back(nodeID);
            ++nonAccessNum;
        }
    }
    printf("\n");
}

Greedy.h:

/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd.                                          *
 * @file: Greedy.h                                                                   *
 * @author: Xu Le, He Jianfan                                                        *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#ifndef __GREEDY_H__
#define __GREEDY_H__

#include "TopologyScanner.h"

#define uuid(n1, n2)    (n1 << 16) + n2

/** Greedy algorithm implemented by a finite state machine(FSM). */
class Greedy
{
public:
    explicit Greedy(TopologyScanner& _scanner);
    ~Greedy();

    Greedy(const Greedy& rhs) = delete;
    Greedy& operator=(const Greedy& rhs) = delete;

    void initialize(std::list<int>& servers);
    bool optimize(std::list<int>& servers, std::vector<int>& serversOutputFlux, long justResultFee, long purchaseFee, long deploymentFee);

private:
    enum Progress {
        ZOM,     ///< Zero Order Minus.
        ZOP,     ///< Zero Order Plus.
        DL,      ///< Decrease Level Operation.
        DLSTEP,  ///< Single Step Of Decrease Level Operation.
        IL,      ///< Increase Level Operation.
        ILSTEP,  ///< Single Step Of Increase Level Operation.
        EX,      ///< Exchange Operation.(neighbor)
        EXSTEP,  ///< Single Step Of Exchange Operation.(neighbor)
        EX2,     ///< Exchange Operation.(good)
        EX2STEP, ///< Single Step Of Exchange Operation.(good)
        RM       ///< Remove Operation.
    };

    struct OverFluxTuple
    {
        OverFluxTuple(double _unitFee, int _nodeID, int _preLevel) : unitFee(_unitFee), nodeID(_nodeID), preLevel(_preLevel) {}

        double unitFee; ///< unit over flux fee.
        int nodeID;     ///< node ID.
        int preLevel;   ///< hardware level before decrease operation.
    };

private:
    TopologyScanner &scanner; ///< reference to topologyScanner

    enum Progress progress;   ///< indicates current optimize progress.

    uint32_t minusPlusTimes;  ///< times of executed zero order minus and zero order plus progress.
    const uint32_t MAX_MINUS_PLUS_TIMES; ///< max allowed times of executing zero order minus and zero order plus progress.
    const int MAX_TRY_TIMES_IN_EX;  ///< max allowed trying times of exchanging a certain pair of nodes.

    int curIdx;          ///< current possible consumption node index.
    uint32_t curNBIdx;   ///< current selected exchangeable neighbor node index.
    uint32_t cspCN;      ///< only count those possible consumption node.
    uint32_t neighborCN; ///< only count those exchangeable neighbor node.

    bool *deployed;      ///< if node x has deployed a server, then deployed[i] is true, otherwise false.
    int *cspRank;        ///< consumption rank used in zero order plus and zero order minus progress.

    bool hasExecutedZOP;       ///< whether ZOP progress has been executed.
    bool exchangeSwitch;       ///< true indicates adapt flow through rule in EX2, false indicates adapt unit flux fee rule in EX2.
    bool inDecreaseProgress;   ///< control which part of code to be executed when state just switched to decrease progress.
    bool inIncreaseProgress;   ///< control which part of code to be executed when state just switched to increase progress.
    bool inExchangeProgress;   ///< control which part of code to be executed when state just switched to exchange progress.
    bool inRemovingProgress;   ///< control which part of code to be executed when state just switched to removing progress.
    int tryDecreaseTimes;      ///< store the number we have tried to decrease server level.
    int tryIncreaseTimes;      ///< store the number we have tried to increase server level.
    int tryExchangeTimes;      ///< store the number we have tried to exchange, it is used to resume curIdx when return to exchange operation.
    uint32_t decreasableIdx;   ///< current index of decreasable node.
    uint32_t decreasableNum;   ///< number of decreasable node.
    uint32_t increasableIdx;   ///< current index of increasable node.
    uint32_t increasableNum;   ///< number of increasable node.
    uint32_t exchangeableIdx;  ///< current index of exchangeable node.
    uint32_t exchangeableCIdx; ///< current index of exchangeable node.
    uint32_t exchangeableNum;  ///< number of exchangeable node.
    uint32_t exchangeableCN;   ///< number of exchangeable node.
    uint32_t removableIdx;     ///< current index of removable node.
    uint32_t removableNum;     ///< number of removable node.
    const uint32_t kDecreasableNum;  ///< const number of decreasable node.
    const uint32_t kIncreasableNum;  ///< const number of increasable node.
    const uint32_t kExchangeableNum; ///< const number of exchangeable node.
    const uint32_t kExchangeableCN;  ///< const number of exchangeable node.
    const uint32_t kRemovableNum;    ///< const number of removable node.

    int lastChangedNode;      ///< store the node in previous minus operation for undo operation.
    long lastResultFee;       ///< store the total fee result by previous operation for comparsion purpose.

    std::vector<double> deltaPrice; ///< delta price of two neighbor level, deltaPrice[i] == priceTable[i+1] - priceTable[i], where i >= 0
    std::vector<std::pair<int, int> > decreasableArray; ///< decreasable node array.
    std::vector<std::pair<int, int> > increasableArray; ///< increasable node array.
    std::vector<int> exchangeableArray;    ///< exchangeable node array.
    std::vector<int> neighborArray;        ///< store exchangeable neighbors.
    std::vector<int> exchangeableCArray;   ///< exchangeable node array.
    std::vector<int> removableArray;       ///< removable node array.
    std::unordered_set<int> serverHashset; ///< store current server deployment nodes.
    std::unordered_map<int /* uuid */, int /* times */> exchangeTimes; ///< store how many times a pair of node was exchanged.
    std::list<int>::iterator itS;          ///< iterate servers list for remove a certain server.
};

#endif /* __GREEDY_H__ */

Greedy.cpp:

/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd.                                          *
 * @file: Greedy.cpp                                                                 *
 * @author: Xu Le, He Jianfan                                                        *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#include "Greedy.h"

#define REMOVE_ACCESS_NODE     for (itS = servers.begin(); itS != servers.end(); ++itS) { if (*itS == cspRank[curIdx]) { servers.erase(itS); break; } }
#define REMOVE_CHANGED_NODE    for (itS = servers.begin(); itS != servers.end(); ++itS) { if (*itS == lastChangedNode) { servers.erase(itS); break; } }

extern int log_level;

extern uint32_t networkNodeNum;
extern uint32_t linkNum;
extern uint32_t consumptionNodeNum;
extern uint32_t hardwareLevelNum;

extern std::vector<int> priceTable;
extern std::vector<int> outcapTable;
extern std::vector<int> deployTable;
extern std::vector<int> accessTable;
extern std::vector<int> demandTable;
extern std::unordered_map<int, int> provideTable;

extern std::list<int> curBestServers;

Greedy::Greedy(TopologyScanner& _scanner) : scanner(_scanner), progress(Progress::ZOM),
    minusPlusTimes(0), MAX_MINUS_PLUS_TIMES(networkNodeNum/600+1), MAX_TRY_TIMES_IN_EX(15), curIdx(0), curNBIdx(0),
    hasExecutedZOP(false), exchangeSwitch(false), inDecreaseProgress(false), inIncreaseProgress(false), inExchangeProgress(false), inRemovingProgress(false),
    kDecreasableNum(networkNodeNum/20), kIncreasableNum(networkNodeNum/20), kExchangeableNum(networkNodeNum/20), kExchangeableCN(networkNodeNum/20), kRemovableNum(5)
{
    deployed = new bool[networkNodeNum];
    cspCN = static_cast<uint32_t>(scanner.possibleServers.size());
    cspRank = new int[cspCN];
    tryDecreaseTimes = static_cast<int>(networkNodeNum) / 6; // needn't to be changed
    tryIncreaseTimes = static_cast<int>(networkNodeNum) / 6; // needn't to be changed

    decreasableArray.resize(kDecreasableNum);
    increasableArray.resize(kIncreasableNum);
    exchangeableArray.resize(kExchangeableNum);
    neighborArray.resize(1024);
    exchangeableCArray.resize(kExchangeableCN);
    removableArray.resize(kRemovableNum);
}

Greedy::~Greedy()
{
    delete []deployed;
    delete []cspRank;
}

/** API function of CDN server list initialization required by deploy.cpp. */
void Greedy::initialize(std::list<int>& servers)
{
    printf("====================    Enter greedy category    ====================\n");

    uint32_t i = 0;

    progress = Progress::ZOM;
    minusPlusTimes = 0;
    curIdx = 0;
    curNBIdx = 0;
    hasExecutedZOP = false;
    exchangeSwitch = false;
    inDecreaseProgress = false;
    inIncreaseProgress = false;
    inExchangeProgress = false;
    inRemovingProgress = false;
    tryDecreaseTimes = static_cast<int>(networkNodeNum) / 6; // needn't to be changed
    tryIncreaseTimes = static_cast<int>(networkNodeNum) / 6; // needn't to be changed
    for (i = 0; i < networkNodeNum; ++i)
        deployed[i] = false;
    deltaPrice.resize(hardwareLevelNum);
    deltaPrice[0] = priceTable[0];
    for (i = 1; i < hardwareLevelNum; ++i)
        deltaPrice[i] = priceTable[i] - priceTable[i-1];
    exchangeTimes.clear();

    for (i = 0; i < cspCN; ++i)
        cspRank[i] = scanner.possibleServers[cspCN-i-1];

    info_log("====================    Progress::ZOM(1) starts    ====================\n");

    for (i = 1; i < cspCN; ++i) // the least rank node has been removed
    {
        servers.push_back(cspRank[i]);
        deployed[cspRank[i]] = true;
    }

    for (std::set<int>::iterator itDef = scanner.definiteServers.begin(); itDef != scanner.definiteServers.end(); ++itDef)
    {
        servers.push_back(*itDef);
        deployed[*itDef] = true;
    }

    lastChangedNode = cspRank[curIdx];
    // set initial total fee to infinity so that optimize() progress can execute normally, though the lease rank node will be removed without examination
    lastResultFee = INF;
}

/**
 * @brief API function of CDN server list optimization required by deploy.cpp.
 * 
 * 1. initial state is Progress::ZOM and bool variable hasExecutedZOP == false, thus starts at line 118, when it is time to exit this state, then go to line 127,137;
 * 2. when state switched to Progress::ZOP, starts at line 163, when it is time to exit this state, set bool variable hasExecutedZOP = true, then go to line 180,184;
 * 3. when state switched back to Progress::ZOM, starts at line 86, when it is time to exit this state, then go to line 111;
 */
bool Greedy::optimize(std::list<int>& servers, std::vector<int>& serversOutputFlux, long justRentalFee, long purchaseFee, long deploymentFee)
{
    uint32_t i = 0;
    long justResultFee = justRentalFee + purchaseFee + deploymentFee;

    switch (progress)
    {
    case Progress::ZOM:
    {
        if (hasExecutedZOP) // usually will enter this part
        {
            if (lastResultFee <= justResultFee || justRentalFee == -1) // restore the most recently removed node
            {
                servers.push_front(lastChangedNode);
                deployed[lastChangedNode] = true;
            }
            else
                lastResultFee = justResultFee;

            while (!deployed[cspRank[curIdx]] && curIdx < static_cast<int>(cspCN-1))
                ++curIdx;
            if (curIdx < static_cast<int>(cspCN-1)) // quit above while loop due to the first condition
            {
                REMOVE_ACCESS_NODE
                deployed[cspRank[curIdx]] = false;
                lastChangedNode = cspRank[curIdx++];
            }
            else
            {
                if (++minusPlusTimes < MAX_MINUS_PLUS_TIMES)
                    curIdx = -1; // prepare to start ZOP progress
                else
                    curIdx = -2; // prepare to start DL progress
                hasExecutedZOP = false;
            }
        }
        else if (curIdx > -1) // only the initial state ZOM will enter this part
        {
            if (lastResultFee <= justResultFee || justRentalFee == -1) // restore the most recently removed node
            {
                servers.push_front(lastChangedNode);
                deployed[lastChangedNode] = true;
            }
            else
                lastResultFee = justResultFee;

            if (++curIdx >= static_cast<int>(cspCN-1))
                curIdx = -1; // prepare to start ZOP progress
            else
            {
                REMOVE_ACCESS_NODE
                deployed[cspRank[curIdx]] = false;
                lastChangedNode = cspRank[curIdx];
            }
        }
        else if (curIdx > -2) // actually here is a transitional state when the next loop will switch to Progress::ZOP
        {
            lastResultFee = justResultFee;

            info_log("Total fee after Progress::ZOM(%u) is %ld\n\n", minusPlusTimes, lastResultFee);
            info_log("====================    Progress::ZOP(%u) starts    ====================\n", minusPlusTimes);

            curIdx = 0; // return to 0, loop restarts again
            while (deployed[cspRank[curIdx]])
                ++curIdx;

            servers.push_front(cspRank[curIdx]);
            deployed[cspRank[curIdx]] = true;
            lastChangedNode = cspRank[curIdx++];

            progress = Progress::ZOP;
        }
        else // actually here is a transitional state when the next loop will switch to Progress::DL
        {
            lastResultFee = justResultFee;

            servers = curBestServers;
            info_log("Total fee after Progress::ZOM(%u) is %ld\n\n", minusPlusTimes, lastResultFee);
            info_log("====================    Progress::DL starts    ====================\n");

            curIdx = 0;
            progress = Progress::DL;
        }
        break;
    }
    case Progress::ZOP:
    {
        if (!hasExecutedZOP) // each time when state has just switched to zero order plus, hasExecutedZOP is always false, see line 116
        {
            if (lastResultFee <= justResultFee || justRentalFee == -1) // remove the most recently added node
            {
                servers.pop_front();
                deployed[lastChangedNode] = false;
            }
            else
                lastResultFee = justResultFee;

            while (deployed[cspRank[curIdx]] && curIdx < static_cast<int>(cspCN-1))
                ++curIdx;
            if (curIdx < static_cast<int>(cspCN-1)) // quit above while loop due to the first condition
            {
                servers.push_front(cspRank[curIdx]);
                deployed[cspRank[curIdx]] = true;
                lastChangedNode = cspRank[curIdx++];
            }
            else
                hasExecutedZOP = true;
        }
        else
        {
            lastResultFee = justResultFee;

            info_log("Total fee after Progress::ZOP(%u) is %ld\n\n", minusPlusTimes, lastResultFee);
            info_log("====================    Progress::ZOM(%u) starts    ====================\n", minusPlusTimes+1);

            curIdx = 0; // return to 0, loop restarts again
            while (!deployed[cspRank[curIdx]])
                ++curIdx;

            REMOVE_ACCESS_NODE
            deployed[cspRank[curIdx]] = false;
            lastChangedNode = cspRank[curIdx++];

            progress = Progress::ZOM;
        }
        break;
    }
    case Progress::DL:
    {
        if (!inDecreaseProgress)
        {
            inDecreaseProgress = true;
            // info_log("====================    Progress::DL starts    ====================\n");
            // info_log("DL --- justResultFee: %ld\n", justResultFee);
            --tryDecreaseTimes;

            std::vector<struct OverFluxTuple> sortOverFluxFee;
            sortOverFluxFee.reserve(servers.size());
            for (itS = servers.begin(), i = 0; itS != servers.end(); ++itS, ++i)
            {
                int curLevel = fitLevel(serversOutputFlux[i]);
                if (curLevel > 0 && serversOutputFlux[i] < outcapTable[curLevel])
                {
                    int overFlux = serversOutputFlux[i] - outcapTable[curLevel-1];
                    sortOverFluxFee.emplace_back(deltaPrice[curLevel]/overFlux, *itS, curLevel);
                }
            }
            std::sort(sortOverFluxFee.begin(), sortOverFluxFee.end(), [](const struct OverFluxTuple& lhs, const struct OverFluxTuple& rhs) { return lhs.unitFee > rhs.unitFee; });
            decreasableNum = std::min(kDecreasableNum, static_cast<uint32_t>(sortOverFluxFee.size()));
            for (i = 0; i < decreasableNum; ++i)
            {
                decreasableArray[i].first = sortOverFluxFee[i].nodeID;
                decreasableArray[i].second = sortOverFluxFee[i].preLevel;
            }
            decreasableIdx = 0;
        }

        lastChangedNode = decreasableArray[decreasableIdx].first;
        int postLevel = decreasableArray[decreasableIdx++].second - 1;
        if (tryDecreaseTimes < 0)
        {
            printf("====================    Exit greedy category    ====================\n\n");
            return false;
        }

        scanner.graph.setOutputFluxUpbound(lastChangedNode, outcapTable[postLevel]);
        progress = Progress::DLSTEP;
        break;
    }
    case Progress::DLSTEP:
    {
        if (lastResultFee <= justResultFee || justRentalFee == -1) // revert to unrestricted output capacity
        {
            int preLevel = decreasableArray[decreasableIdx-1].second;
            scanner.graph.setOutputFluxUpbound(lastChangedNode, outcapTable[preLevel]);
            if (decreasableIdx < decreasableNum)
            {
                lastChangedNode = decreasableArray[decreasableIdx].first;
                int postLevel = decreasableArray[decreasableIdx].second - 1;
                scanner.graph.setOutputFluxUpbound(lastChangedNode, outcapTable[postLevel]);
                ++decreasableIdx;
            }
            else
            {
                inDecreaseProgress = false;
                progress = networkNodeNum < 1000 ? Progress::IL : Progress::EX;
            }
        }
        else
        {
            lastResultFee = justResultFee;

            info_log("[%d]'s level decreased in Progress::DL\t---  %ld\n", lastChangedNode, justResultFee);

            inDecreaseProgress = false;
            progress = Progress::DL; // return back to Progress::DL and recalculate unit over flux fee
        }
        break;
    }
    case Progress::IL:
    {
        if (!inIncreaseProgress)
        {
            inIncreaseProgress = true;
            // info_log("====================    Progress::IL starts    ====================\n");
            // info_log("IL --- justResultFee: %ld\n", justResultFee);
            --tryIncreaseTimes;

            decreasableIdx = 0;

            std::vector<struct OverFluxTuple> sortOverFluxFee;
            sortOverFluxFee.reserve(servers.size());
            for (itS = servers.begin(), i = 0; itS != servers.end(); ++itS, ++i)
            {
                int curLevel = fitLevel(serversOutputFlux[i]);
                if (curLevel < static_cast<int>(hardwareLevelNum)-1 && serversOutputFlux[i] == outcapTable[curLevel])
                {
                    int overFlux = serversOutputFlux[i] - outcapTable[curLevel-1];
                    sortOverFluxFee.emplace_back(deltaPrice[curLevel]/overFlux, *itS, curLevel);
                }
            }
            std::sort(sortOverFluxFee.begin(), sortOverFluxFee.end(), [](const struct OverFluxTuple& lhs, const struct OverFluxTuple& rhs) { return lhs.unitFee < rhs.unitFee; });
            increasableNum = std::min(kIncreasableNum, static_cast<uint32_t>(sortOverFluxFee.size()));
            for (i = 0; i < increasableNum; ++i)
            {
                increasableArray[i].first = sortOverFluxFee[i].nodeID;
                increasableArray[i].second = sortOverFluxFee[i].preLevel;
            }
        }

        lastChangedNode = decreasableArray[decreasableIdx].first;
        int postDecreaseLevel = decreasableArray[decreasableIdx].second - 1;
        if (++decreasableIdx > decreasableNum)
        {
            inIncreaseProgress = false;
            progress = Progress::EX;
            return true;
        }

        increasableIdx = 0;
        if (lastChangedNode == increasableArray[increasableIdx].first)
            ++increasableIdx;

        int curUpgradedNode = increasableArray[increasableIdx].first;
        int postIncreaseLevel = increasableArray[increasableIdx].second + 1;
        scanner.graph.setOutputFluxUpbound(lastChangedNode, outcapTable[postDecreaseLevel]);
        scanner.graph.setOutputFluxUpbound(curUpgradedNode, outcapTable[postIncreaseLevel]);
        ++increasableIdx;
        progress = Progress::ILSTEP;
        break;
    }
    case Progress::ILSTEP:
    {
        int lastUpgradedNode = increasableArray[increasableIdx-1].first;
        if (lastResultFee <= justResultFee || justRentalFee == -1) // revert to unrestricted output capacity and unupgrade output capacity
        {
            int preIncreaseLevel = increasableArray[increasableIdx-1].second;
            scanner.graph.setOutputFluxUpbound(lastUpgradedNode, outcapTable[preIncreaseLevel]); // undo previous upgrade operation
            if (lastChangedNode == increasableArray[increasableIdx].first)
                ++increasableIdx;
            if (increasableIdx < increasableNum) // try to upgrade the next increasable node
            {
                int curUpgradedNode = increasableArray[increasableIdx].first;
                int postIncreaseLevel = increasableArray[increasableIdx].second + 1;
                scanner.graph.setOutputFluxUpbound(curUpgradedNode, outcapTable[postIncreaseLevel]);
                ++increasableIdx;
            }
            else
            {
                int preDecreaseLevel = decreasableArray[decreasableIdx-1].second;
                scanner.graph.setOutputFluxUpbound(lastChangedNode, outcapTable[preDecreaseLevel]);

                progress = Progress::IL; // return back to Progress::IL and try the next decreasable node
            }
        }
        else
        {
            lastResultFee = justResultFee;

            info_log("[%d]'s level decreased in Progress::IL\t---  %ld\n", lastChangedNode, justResultFee);
            info_log("[%d]'s level increased in Progress::IL\t---  %ld\n", lastUpgradedNode, justResultFee);

            inIncreaseProgress = false;
            progress = Progress::EX; // go to Progress::DL and try to decrease server's level in new scheme
        }
        break;
    }
    case Progress::EX:
    {
        if (!inExchangeProgress)
        {
            inExchangeProgress = true;
            // info_log("====================    Progress::EX starts    ====================\n");
            // info_log("EX --- justResultFee: %ld\n", justResultFee);

            std::vector<std::pair<double /* unit flux fee */, int /* node ID */> > sortUnitFluxFee;
            sortUnitFluxFee.reserve(servers.size());
            serverHashset.clear();
            for (itS = servers.begin(), i = 0; itS != servers.end(); ++itS, ++i)
            {
                serverHashset.insert(*itS);
                int curLevel = fitLevel(serversOutputFlux[i]);
                double totalNodeFee = priceTable[curLevel] + deployTable[*itS];
                sortUnitFluxFee.emplace_back(totalNodeFee/serversOutputFlux[i], *itS);
                // printf("%d[%.2f] ", *itS, totalNodeFee/serversOutputFlux[i]);
            }
            // printf("\n");
            std::sort(sortUnitFluxFee.begin(), sortUnitFluxFee.end(), std::greater<std::pair<double, int> >());
            exchangeableNum = std::min(kExchangeableNum, static_cast<uint32_t>(sortUnitFluxFee.size()));
            for (i = 0; i < exchangeableNum; ++i)
                exchangeableArray[i] = sortUnitFluxFee[i].second;
            exchangeableIdx = 0;
        }

        lastChangedNode = exchangeableArray[exchangeableIdx];
        if (++exchangeableIdx > exchangeableNum)
        {
            inExchangeProgress = false;
            progress = Progress::EX2;
            scanner.graph.setExchangeOperation(exchangeSwitch);
            return true;
        }

        neighborCN = 0;
        // printf("%d:  ", lastChangedNode);
        struct Arc *parc = NULL;
        for (parc = scanner.graph.NodeList[lastChangedNode].firstArc; parc != NULL; parc = parc->nextArc)
        {
            if (parc->dstID < scanner.mNetworkNodeNum
                && deployTable[parc->dstID] <= deployTable[lastChangedNode]
                && scanner.nodeLevel[parc->dstID] >= scanner.nodeLevel[lastChangedNode]
                && serverHashset.find(parc->dstID) == serverHashset.end())
            {
                // printf("%d[%d] -> ", parc->dstID, scanner.nodeLevel[parc->dstID]);
                neighborArray[neighborCN++] = parc->dstID;
            }
        }
        // printf("\n");
        if (neighborCN > 0)
        {
            curNBIdx = 0;
            while (curNBIdx < neighborCN && ++exchangeTimes[uuid(lastChangedNode, neighborArray[curNBIdx])] > MAX_TRY_TIMES_IN_EX)
                ++curNBIdx;

            if (curNBIdx < neighborCN)
            {
                REMOVE_CHANGED_NODE
                deployed[lastChangedNode] = false;
                servers.push_front(neighborArray[curNBIdx]);
                deployed[neighborArray[curNBIdx]] = true;
                ++curNBIdx;

                progress = Progress::EXSTEP; // go to Progress::EXSTEP and try to exchange with neighbors one by one
            }
        }
        break;
    }
    case Progress::EXSTEP:
    {
        if (lastResultFee <= justResultFee || justRentalFee == -1) // try to exchange with another neighbor or restore the exchangeable node
        {
            servers.pop_front(); // remove the neighbor node added in previous exchange operation
            deployed[neighborArray[curNBIdx-1]] = false;
            while (curNBIdx < neighborCN && ++exchangeTimes[uuid(lastChangedNode, neighborArray[curNBIdx])] > MAX_TRY_TIMES_IN_EX)
                ++curNBIdx;
            if (curNBIdx < neighborCN)
            {
                servers.push_front(neighborArray[curNBIdx]); // add another neighbor
                deployed[neighborArray[curNBIdx]] = true;
                ++curNBIdx;
            }
            else
            {
                servers.push_front(lastChangedNode); // restore the exchangeable node removed in previous exchange operation
                deployed[lastChangedNode] = true;
                progress = Progress::EX; // return back to Progress::EX and try the next exchangeable node
            }
        }
        else
        {
            lastResultFee = justResultFee;

            info_log("node [%d] <-> [%d] in Progress::EX  \t---  %ld\n", lastChangedNode, neighborArray[curNBIdx-1], justResultFee);

            inExchangeProgress = false;
            progress = Progress::DL; // go to Progress::DL and try to decrease server's level in new scheme
        }
        break;
    }
    case Progress::EX2:
    {
        if (!inExchangeProgress)
        {
            inExchangeProgress = true;
            info_log("====================    Progress::EX2 starts    ====================\n");
            // info_log("EX2 --- justResultFee: %ld\n", justResultFee);

            exchangeableIdx = 0;

            if (exchangeSwitch)
            {
                scanner.graph.setExchangeOperation(false);
                std::vector<std::pair<int /* flux through */, int /* node ID */> > sortFluxThrough;
                sortFluxThrough.reserve(networkNodeNum-servers.size());
                for (int l = 0; l < scanner.mNetworkNodeNum; ++l)
                    if (serverHashset.find(l) == serverHashset.end())
                        sortFluxThrough.emplace_back(scanner.graph.fluxThroughNode[l], l);
                std::sort(sortFluxThrough.begin(), sortFluxThrough.end(), std::greater<std::pair<int, int> >());
                exchangeableCN = std::min(kExchangeableCN, static_cast<uint32_t>(sortFluxThrough.size()));
                for (i = 0; i < exchangeableCN; ++i)
                    exchangeableCArray[i] = sortFluxThrough[i].second;
            }
            else
            {
                std::vector<std::pair<double /* unit flux fee */, int /* node ID */> > sortUnitFluxFee;
                sortUnitFluxFee.reserve(networkNodeNum-servers.size());
                for (int l = 0; l < scanner.mNetworkNodeNum; ++l)
                {
                    if (serverHashset.find(l) == serverHashset.end())
                    {
                        double totalNodeFee = priceTable[scanner.nodeLevel[l]] + deployTable[l];
                        sortUnitFluxFee.emplace_back(totalNodeFee/outcapTable[scanner.nodeLevel[l]], l);
                    }
                }
                std::sort(sortUnitFluxFee.begin(), sortUnitFluxFee.end());
                exchangeableCN = std::min(kExchangeableCN, static_cast<uint32_t>(sortUnitFluxFee.size()));
                for (i = 0; i < exchangeableCN; ++i)
                    exchangeableCArray[i] = sortUnitFluxFee[i].second;
            }
            exchangeSwitch = exchangeSwitch == false;
        }

        lastChangedNode = exchangeableArray[exchangeableIdx];
        if (++exchangeableIdx > exchangeableNum)
        {
            inExchangeProgress = false;
            progress = Progress::RM;
            return true;
        }

        exchangeableCIdx = 0;
        while (exchangeableCIdx < exchangeableCN && ++exchangeTimes[uuid(lastChangedNode, exchangeableCArray[exchangeableCIdx])] > MAX_TRY_TIMES_IN_EX)
            ++exchangeableCIdx;

        if (exchangeableCIdx < exchangeableCN)
        {
            REMOVE_CHANGED_NODE
            deployed[lastChangedNode] = false;
            servers.push_front(exchangeableCArray[exchangeableCIdx]);
            deployed[exchangeableCArray[exchangeableCIdx]] = true;
            ++exchangeableCIdx;

            progress = Progress::EX2STEP; // go to Progress::EX2STEP and try to exchange with exchangeable candidate nodes one by one
        }
        break;
    }
    case Progress::EX2STEP:
    {
        if (lastResultFee <= justResultFee || justRentalFee == -1) // try to exchange with exchangeable candidate node or restore the exchangeable node
        {
            servers.pop_front(); // remove the exchangeable candidate node added in previous exchange operation
            deployed[exchangeableCArray[exchangeableCIdx-1]] = false;
            while (exchangeableCIdx < exchangeableCN && ++exchangeTimes[uuid(lastChangedNode, exchangeableCArray[exchangeableCIdx])] > MAX_TRY_TIMES_IN_EX)
                ++exchangeableCIdx;
            if (exchangeableCIdx < exchangeableCN)
            {
                servers.push_front(exchangeableCArray[exchangeableCIdx]); // add another exchangeable candidate node
                deployed[exchangeableCArray[exchangeableCIdx]] = true;
                ++exchangeableCIdx;
            }
            else
            {
                servers.push_front(lastChangedNode); // restore the exchangeable node removed in previous exchange operation
                deployed[lastChangedNode] = true;
                progress = Progress::EX2; // return back to Progress::EX2 and try the next exchangeable node
            }
        }
        else
        {
            lastResultFee = justResultFee;

            info_log("node [%d] <-> [%d] in Progress::EX2  \t---  %ld\n", lastChangedNode, exchangeableCArray[exchangeableCIdx-1], justResultFee);

            inExchangeProgress = false;
            progress = Progress::DL; // go to Progress::DL and try to decrease server's level in new scheme
        }
        break;
    }
    case Progress::RM:
    {
        if (!inRemovingProgress)
        {
            inRemovingProgress = true;
            info_log("====================    Progress::RM starts    ====================\n");

            std::vector<std::pair<double /* unit flux fee */, int /* node ID */> > sortUnitFluxFee;
            sortUnitFluxFee.reserve(servers.size());
            for (itS = servers.begin(), i = 0; itS != servers.end(); ++itS, ++i)
            {
                int curLevel = fitLevel(serversOutputFlux[i]);
                double totalNodeFee = priceTable[curLevel] + deployTable[*itS];
                sortUnitFluxFee.emplace_back(totalNodeFee/serversOutputFlux[i], *itS);
                // printf("%d[%.2f] ", *itS, totalNodeFee/serversOutputFlux[i]);
            }
            // printf("\n");
            std::sort(sortUnitFluxFee.begin(), sortUnitFluxFee.end(), std::greater<std::pair<double, int> >());
            removableNum = std::min(kRemovableNum, static_cast<uint32_t>(sortUnitFluxFee.size()));
            for (i = 0; i < removableNum; ++i)
            {
                removableArray[i] = sortUnitFluxFee[i].second;
                // printf("%f %d %d\n", sortUnitFluxFee[i].first, removableArray[i], scanner.nodeLevel[removableArray[i]]);
            }
            removableIdx = 0;
            lastChangedNode = removableArray[removableIdx++];
            REMOVE_CHANGED_NODE
            deployed[lastChangedNode] = false;
        }
        else
        {
            if (lastResultFee <= justResultFee || justRentalFee == -1) // try to restore the removable node
            {
                servers.push_front(lastChangedNode);
                deployed[lastChangedNode] = true;
                if (removableIdx < removableNum)
                {
                    lastChangedNode = removableArray[removableIdx++];
                    REMOVE_CHANGED_NODE
                    deployed[lastChangedNode] = false;
                }
                else
                {
                    printf("====================    Exit greedy category    ====================\n\n");
                    return false;
                }
            }
            else
            {
                lastResultFee = justResultFee;

                info_log("node [%d] is successfully removed in Progress::RM  ---  %ld\n", lastChangedNode, justResultFee);

                inRemovingProgress = false;
                progress = Progress::DL; // go to Progress::DL and try to decrease server's level in new scheme
            }
        }
        break;
    }
    default:
        error_log("Unknown progress!\n");
    }

    return true;
}

main.cpp:

/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd. *
 * @file: main.cpp *
 * @author: Xu Le, He Jianfan *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#include "Greedy.h"

extern int log_level;

extern uint32_t networkNodeNum;
extern uint32_t linkNum;
extern uint32_t consumptionNodeNum;
extern uint32_t hardwareLevelNum;

extern std::vector<int> priceTable;
extern std::vector<int> outcapTable;
extern std::vector<int> deployTable;
extern std::vector<int> accessTable;
extern std::vector<int> demandTable;
extern std::unordered_map<int, int> provideTable;

std::list<int> curBestServers;

int main(int argc, char *argv[])
{
/*************************************************************************************
 * @copyright: Huawei Technologies Co. Ltd.                                          *
 * @file: main.cpp                                                                   *
 * @author: Xu Le, He Jianfan                                                        *
 * This file is part of 2017 Huawei CodeCraft solution <http://codecraft.huawei.com> *
 *************************************************************************************/
#include "Greedy.h"

extern int log_level;

extern uint32_t networkNodeNum;
extern uint32_t linkNum;
extern uint32_t consumptionNodeNum;
extern uint32_t hardwareLevelNum;

extern std::vector<int> priceTable;
extern std::vector<int> outcapTable;
extern std::vector<int> deployTable;
extern std::vector<int> accessTable;
extern std::vector<int> demandTable;
extern std::unordered_map<int, int> provideTable;

std::list<int> curBestServers;

int main(int argc, char *argv[])
{
    Timer procedureTimer("Elapsed Time: ");
    /*******************************************************************
     ********************     Time elapsing ...     ********************
     *******************************************************************/
    // printf("current log level: %s\n\n", getLogLevel());

    std::list<struct LinkTuple> linkTuples;

    // 1. ===============    parse input char* array    ===============
    parseInput(argv[1], linkTuples);

    if (networkNodeNum > 1000)
        defineAvailableLevel();
    uint32_t bestRestrictedLevelNum = hardwareLevelNum;

    debug_log("NetworkNodeNum: %u, LinkNum: %u, ConsumptionNodeNum: %u, HardwareLevelNum: %u\n", networkNodeNum, linkNum, consumptionNodeNum, hardwareLevelNum);

    {
        // Complexity defaultComplexity = defineDefaultComplexity(); // local variable, just print the complexity for debug, not used to optimize case according to its size
        // info_log("default complexity: %s\n", complexityString(defaultComplexity));
    }

    linkTuples.sort([](const struct LinkTuple& lhs, const struct LinkTuple& rhs) { return lhs.src < rhs.src; });

    // 2. ===============    construct graph    ===============
    Graph graph(networkNodeNum);
    graph.construct(linkTuples);
    graph.undirectify(linkTuples);
    linkTuples.clear(); // we don't need it anymore, release its memory

    // 3. ===============    display graph    ===============
    // graph.display(); // log control inside

    // 4. ===============    add minimum cost flow algorithm related components into graph    ===============
    graph.preMinimumCostFlow();

    // 5. ===============    critical part of finding a server deployment solution    ===============
    uint32_t i = 0;
    std::list<int> newServers, bestServers;
    std::vector<int> serversOutputFlux(networkNodeNum, 0);
    std::vector<int> restrictedOutputCap(networkNodeNum, 0);
    std::vector<int> gRestrictedOutputCap(networkNodeNum, 0);

    // 5.1. ==========    check whether the problen is unfeasible    ==========
    for (i = 0; i < networkNodeNum; ++i)
        newServers.push_back(i);

    // 5.1.1. =====    add the super source of all network nodes' union onto graph    =====
    graph.addSuperSource(newServers);

    // 5.1.2. =====    calculate minimum cost flow using all network nodes    =====
    long lowboundRentalFee = graph.minimumCostFlow(serversOutputFlux);

    // 5.1.3. =====    delete the super source from graph and revert the status of arc to initialization    =====
    graph.delSuperSource();

    // 5.1.4. =====    check whether no feasible scheme can be found    =====
    if (lowboundRentalFee == -1)
    {
        uncond_log("This case is unfeasible, write NA to result file.\n");

        // 5.1.5. =====    delete minimum cost flow algorithm related components from graph    =====
        graph.postMinimumCostFlow();

        // 5.1.6. =====    destruct graph    =====
        graph.destruct();

        // 5.1.7. =====    output NA to result file    =====
        outputToFile(argv[2]);

        return 0; // no need to execute rest code, thus return here
    }
    else
        newServers.clear();

    // 5.2. ==========    scan topology to obtain combinations of some carefully selected network nodes as initial scheme    ==========
    TopologyScanner topoScanner(graph);

    topoScanner.scan();
    topoScanner.recommend();

    // 5.3. ==========    deploy CDN servers on one of these combinations of network nodes    ==========
    Greedy greedy(topoScanner);

    long gMinTotalFee = INF;
    double elapsedTime = 0.0;
    std::list<int>::iterator itS;
    int LOOP = networkNodeNum < 1000 ? 3 : 1;

do {
    greedy.initialize(newServers);

    // info_log("initial servers: ");
    // printList(newServers, "");

    int loop = 1;
    long minTotalFee = INF;
    bool continueLoop = true;
    while (true)
    {
        // 5.4. ==========    add a super source onto graph    ==========
        graph.addSuperSource(newServers);

        // 5.5. ==========    execute minimum cost flow algorithm    ==========
        long rentalFee = graph.minimumCostFlow(serversOutputFlux);
        long purchaseFee = 0;
        long deploymentFee = 0;
        if (rentalFee != -1)
        {
            for (itS = newServers.begin(); itS != newServers.end(); ++itS)
                deploymentFee += deployTable[*itS];
            purchaseFee = obtainTotalPurchaseFee(serversOutputFlux, newServers.size());
        }

        // info_log("Link fee is %ld, purchase fee is %ld, deploy fee is %ld, sum up %ld.\n", rentalFee, purchaseFee, deploymentFee, rentalFee + purchaseFee + deploymentFee);

        // 5.6. ==========    a better deployment solution ?    ==========
        if (minTotalFee > rentalFee + purchaseFee + deploymentFee && rentalFee != -1)
        {
            minTotalFee = rentalFee + purchaseFee + deploymentFee;
            curBestServers = newServers;
            for (i = 0; i < networkNodeNum; ++i)
                restrictedOutputCap[i] = graph.NodeList[i].restrictedCap;
        }

        continueLoop = greedy.optimize(newServers, serversOutputFlux, rentalFee, purchaseFee, deploymentFee);

        // 5.7. ==========    time is not enough    ==========
        if (loop % 50 == 0)
            elapsedTime = procedureTimer.elapsed();
        if (loop % 1000 == 0)
            uncond_log("Time elapsed: %fs\n", elapsedTime);
        // how many loops do you want?
        if (++loop > 20000 || !continueLoop || elapsedTime > 87.0) // we have to exit loop when only 3 seconds left
        {
            uncond_log("Exit loop: %d\n", loop);
            info_log("current best servers: ");
            printList(curBestServers, "");

            // 5.7.1. =====    delete current super source from graph and revert the status of arc to initialization    =====
            graph.delSuperSource();
            if (LOOP >= 2)
            {
                if (gMinTotalFee > minTotalFee)
                {
                    gMinTotalFee = minTotalFee;
                    bestServers = curBestServers;
                    bestRestrictedLevelNum = hardwareLevelNum;
                    for (i = 0; i < networkNodeNum; ++i)
                        gRestrictedOutputCap[i] = restrictedOutputCap[i];
                }

                newServers.clear();
                --hardwareLevelNum;
                uncond_log("\nhighest available server level limited to %u\n", hardwareLevelNum-1);
                topoScanner.resetRestrictedCap();
                break;
            }
            if (gMinTotalFee > minTotalFee)
            {
                gMinTotalFee = minTotalFee;
                bestServers = curBestServers;
                for (i = 0; i < networkNodeNum; ++i)
                    gRestrictedOutputCap[i] = restrictedOutputCap[i];
            }
            else
                hardwareLevelNum = bestRestrictedLevelNum;

            // 5.7.2. =====    add the super source of best servers' union onto graph    =====
            for (i = 0; i < networkNodeNum; ++i)
                graph.NodeList[i].restrictedCap = gRestrictedOutputCap[i];
            graph.addSuperSource(bestServers);

            // 5.7.3. =====    calculate minimum cost flow using best servers and then record corresponding flow path    =====
            graph.minimumCostFlow(serversOutputFlux);
            graph.recordFlowPath();

            // 5.7.4. =====    in fact, it is not must to revert status and delete super source    =====
            graph.delSuperSource();
            break;
        }

        // 5.8. ==========    delete the super source from graph and revert the status of arc to initialization    ==========
        graph.delSuperSource();

        // info_log("new servers: ");
        // printList(newServers, "");
    }
}
while (--LOOP > 0);

    // 6. ===============    delete minimum cost flow algorithm related components from graph    ===============
    graph.postMinimumCostFlow();

    info_log("Minimum total fee is %ld.\n", gMinTotalFee);

    std::unordered_map<int /* deploy node */, int /* hardware level */> finalChosenLevel;
    itS = bestServers.begin();
    for (size_t l = 0; l < bestServers.size(); ++l, ++itS)
        finalChosenLevel.insert(std::pair<int, int>(*itS, fitLevel(serversOutputFlux[l])));

    // 7. ===============    modify flux path list to meet output requirement    ===============
    for (std::list<std::vector<int> >::iterator itfp = graph.fluxPathList.begin(); itfp != graph.fluxPathList.end(); ++itfp)
    {
        std::vector<int>& fluxPath = *itfp; // alias
        int storeFlux = fluxPath.back();
        fluxPath.pop_back();
        fluxPath.push_back(provideTable[fluxPath.back()]);
        fluxPath.push_back(storeFlux);
        fluxPath.push_back(finalChosenLevel[fluxPath.front()]);
        // printVector(fluxPath, "");
    }

    // 8. ===============    verify servers' deployment, purchase and flux paths scheme    ===============
    long totalRentalFee = 0, totalPurchaseFee = 0, totalDeploymentFee = 0;
    bool schemeOK = verifyScheme(graph, totalRentalFee, totalPurchaseFee);
    for (itS = bestServers.begin(); itS != bestServers.end(); ++itS)
        totalDeploymentFee += deployTable[*itS];
    if (schemeOK == true)
        uncond_log("Congratulations! Total rental fee is %ld, purchase fee is %ld, deployment fee is %ld, sum up %ld.\n",
            totalRentalFee, totalPurchaseFee, totalDeploymentFee, totalRentalFee + totalPurchaseFee + totalDeploymentFee);
    else
        error_log("Shit! The scheme failed to pass verification!\n");

    // 9. ===============    destruct graph    ===============
    graph.destruct();

    // 10. ===============    output flux path list to result file    ===============
    outputToFile(argv[2], graph.fluxPathList);

    /*******************************************************************
     ********************     Time elapsing end     ********************
     *******************************************************************/
}

注:本文开头处的超链接提供下载的代码包中附带有 18 个测试用例,中级 (600 个点)、高级 (1200 个点) 的各 8 个。

Leave a comment

邮箱地址不会被公开。 必填项已用*标注