获得m个节点之间的最大距离时真的很慢(回溯)(c ++)

时间:2015-05-22 23:18:13

标签: c++ algorithm max distance backtracking

我被要求使用Backtracking或Backtracking + Branch and Bound进行练习,其中输入数据是n,m和矩阵(n x n)。 n代表一些人,而m代表来自n的一些人。在矩阵中,它们之间存在距离,i和j之间的距离与j和i不同。

我试图获得m个节点可以获得的最大距离,该距离是所有距离的总和。例如,如果我选择节点1,2和4,则结果为总和:距离(1,2)+距离(2,1)+距离(2,4)+距离(4,2)+距离( 1,4)+距离(4,1)。

我使用了回溯与分支和绑定(迭代,而不是递归),存储节点(我存储当前值和使用的节点的结构),这可能让我得到一个解决方案。这个节点存储de lower和upper bound,我的意思是,如果我继续使用这个节点和他的儿子,我可以获得更低和更高的解决方案。从节点x,我生成所有可能的节点(未使用的节点),并检查此节点是否可以让我找到解决方案,如果没有,则丢弃并擦除该节点。

我已经实现的代码可以实现,但是它确实很慢。 n值和m值较低时,速度很快,但如果使用更高的数字,则速度非常慢。

这是主要功能和使用的其他功能:

void backtracking(int **matrix, int n, int m){
/////////////////////////////////////////////////////
/*
Part where I try to get the minimum/maximum that I can get from the beginning of the problem
*/

// Lists where I store the values from the matrix, sort from the minimum to the maximum, and from
// the maximum to the minimum. The values are the distances, I mean, the sum of matrix[i][j] and 
// matrix[j][i].
list<int> listMinSums; // list of minimum sums
list<int> listMaxSums; // list of maximum sums

int nMinimumSums = floor((m*m - m)/2); // rounding down
int nMaximumSums = ceil((m*m - m)/2); // rounding up
/*  
 * m*m - m = Given m nodes, there are m*m - m sums.
 * 
 * I count matrix[i][j] + matrix[j][i] as one, so there
 * are (m*m - m)/2 sums.
 */

for (int i = 0; i < n; i++){
    for (int j = 0; j < i; j++){
        int x = (matrix[i][j] + matrix[j][i]);
        // to differentiate from the minimum and maximum sums, I use false and true
        aLista(listMinSums, x, nMinimumSums, false); 
        aLista(listMaxSums, x, nMaximumSums, true);
    }
}
int min = 0;
int max = 0;
int contador = 0; // counting in every iteration to not surpassing the minimum/maximum sums
list<int>::iterator it = listMinSums.begin();
while (it != listMinSums.end() && contador < nMinimumSums){
    min += *it;
    it++;
    contador++;
}
contador = 0;

list<int>::iterator it2 = listMaxSums.begin();
while (it2 != listMaxSums.end() && contador < nMaximumSums){
    max += *it2;
    it2++;
    contador++;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

// LLV = List of Live Nodes. Where I store the nodes that are going to
// guide me to the solution
list<nodo*> llv; 

// I do not store the root node, i store the first n nodes, of the first level.
for (int i = 0; i < n; i++){
    nodo *nod = new nodo(n);
    nod ->level = 0;
    //lower bound. It's the lower solution i can get from this node
    nod ->ci = min; 
    // upper bound. The higher solution i can get from this node.
    nod ->cs = max;
    // estimated benefit. If i have to choose from one node or another, i choose the one with higher
    nod ->be = (min+max)/2;
    // The node i is used
    nod ->used[i] = true;
    // Inserting this node to the list of live nodes.
    insert(llv, nod);
}
int solution = 0; // Initial solution
int c = min; // c = variable used to not use a node and his "sons", anymore
while (!empty(llv)){
    nodo *x = erase(llv, n); // erasing the node with the highest estimated benefit from the llv.

    if (x ->cs > c){
        for (int i = 0; i < n; i++){ // Creating every son of the x node...
            if (!(x ->used[i])){ // ... that has not being used yet
                nodo *y = new nodo(n);
                y ->level = x ->level + 1;
                for (int j = 0; j < n; j++){
                    y ->used[j] = x ->used[j];
                }
                y ->used[i] = true;

                // Adding the values. For example, if node 1 and 2 were inserted, and this is the node 4,
                // adding matrix[1][4]+matrix[4][1]+matrix[2][4] + matrix[4][2]
                int acum = 0;
                for (int k = 0; k < n; k++){
                    if (k != i && consult(x ->used, k))
                        acum += matrix[i][k] + matrix[k][i];
                }
                y ->bact = x ->bact + acum;

                // Getting the lower and upper bound of this node y.
                cotas(y, x, i, y ->level, n, matrix);

                y ->be = (y ->ci + y ->cs)/2;

                // Node where i can get the solution
                if (y ->level == (m-1) && y ->bact > solution){
                    solution = y ->bact;
                    if (y ->bact > c)
                        c = y ->bact;
                }
                // Checking if i can update c
                else if (y ->level != (m-1) && y ->cs > c){
                    insert(llv, y);
                    if (y ->ci > c)
                        c = y ->ci;
                }
                else{
                    // i cannot use this node anymore, so i delete it.
                    delete y;
                }
            }
        }
    }
}
cout << solution << endl;

liberacionMemoria(matrix, n); // freeing the memory used in the matrix

}

void liberacionMemoria(int **matriz, int n){
    for (int i = 0; i < n; i++)
        delete[] matriz[i];
    delete[] matriz;
}

void insert(list<nodo*> &t, nodo *x){
    list<nodo*>::iterator it= t.begin();
    t.insert(it, x);
}

/*
 * Getting the node with hightest estimated benefit from the list of live nodes
 * */
nodo* erase (list<nodo*> &t, int n){
    nodo* erased = new nodo(n);

    erased ->level = -1;
    erased ->be = -1;

    list<nodo*>::iterator it= t.begin();
    list<nodo*>::iterator it2;
    while (it != t.end()){
        nodo* aux = *it;
        if (aux ->be > erased ->be){
            it2 = it;
            erased = aux;
        }
        else if (aux ->be == erased ->be && aux ->level > erased ->level){
            it2 = it;
            erased = aux;
        }
        it++;
    }
    t.erase(it2);
    return erased;
}

/*
 * Checking if in the array of nodes used, the node in the x position it's used
 * */
bool consult(bool *nodesUsed, int x){
    if (nodesUsed[x])
        return true;
    return false;
}

bool empty(list<nodo*> &t){
    list<nodo*>::iterator it= t.begin();
    return (it==t.end());
}

bool aLista(list<int> &t, int x, int m, bool MayorAMenor){
    list<int>::iterator it = t.begin();
    int contador = 0;
    while (it != t.end() && contador < m){
        if (!MayorAMenor){ // lower to upper
            if (*it > x){
                t.insert(it, x);
                return true;
            }
        }
        else{
            if (*it < x){
                t.insert(it, x);
                return true;
            }
        }
        contador++;
        it++;
    }
    if (it == t.end() && contador < m){
        t.insert(it, x);
        return true;
    }
    return false;
}


void cotas(nodo *sonNode, nodo *fatherNode, int elegido, int m, int n, int **matriz){
    int max = 0;
    int min = 999;

    // Getting the sums from the chosen node with the already used
    for (int i = 0; i < n; i++){
        if (consult(sonNode ->used, i)){
            if (elegido != i){
                int x = matriz[i][elegido] + matriz[elegido][i];

                if (x > max)
                    max = x;
                if (x < min)
                    min = x;
            }
        }
    }

    min *= (m-1);
    max *= (m-1);
    min += fatherNode -> bact;
    max += fatherNode -> bact;

    sonNode -> ci = fatherNode ->ci - min;
    sonNode -> cs = fatherNode ->cs - max;
}

我认为,n和ma位高的原因真的很慢,它真的很慢,这是因为节点的上限和下限,这是不准确的,但我不知道如何使它变得更好。

我已经有很多天在思考如何做到这一点,并努力,但没有任何作用。

这里有一些例子:

给定n = 4且m = 2且以下矩阵:

0 3 2 4
2 0 4 5
2 1 0 4
2 3 2 0

结果是8.这很有效,而且很快。

但是当n = 40且m = 10时,它永远不会结束......

我希望有人可以帮助我。感谢。

**** ****** EDIT

我可能没有解释清楚。我的疑问是,我怎么知道,从节点x,我可以得到的越少越好。

因为,解决方案节点的长度取决于m,但是如果我选择一些节点或其他节点,解决方案会发生变化,我不知道如何确定从节点获得更少和最大值,但是准确,能够削减其他不指导我解决方案的分支

0 个答案:

没有答案