LintCode 816: Traveling Salesman Problem (著名的旅行商游历NP问题。状态压缩DP经典)

816. Traveling Salesman Problem

Give n cities(labeled from 1 to n), and the undirected road's cost among the cities as a three-tuple [A, B, c](i.e there is a road between city A and city B and the cost is c). We need to find the smallest cost to travel all the cities starting from 1.

Example

Example 1

Input: 
n = 3
tuple = [[1,2,1],[2,3,2],[1,3,3]]
Output: 3
Explanation: The shortest path is 1->2->3

Example 2

Input:
n = 1
tuple = []
Output: 0

Notice

1.A city can only be passed once.
2.You can assume that you can reach all the rest cities.

Input test data (one parameter per line)How to understand a testcase?

解法1:状态压缩DP
dp[state][i]表示经历了状态state表示的城市组合并且终点在i的路线的最小成本。state是用二进制表示的城市集合,1表示城市已经旅行过,0表示还没有旅行过。

注意:
1) 什么样的问题适合状压呢?
a) 状压是指数级,所以这种题目的数据范围都非常小,在20左右。
b) 一般对于每个物品,我们只关心它有没有/是不是,这样的问题。
2)因为是无向图,可能A-B和B-A的长度不一样,我们要把他们归一化,即A-B和B-A的长度是它们之间所有路径的最短值。

时间复杂度O(n^2 * 2^n)。可以看出它是关于n的指数级。
空间复杂度O(n* 2^n)。

class Solution {
public:
    /**
     * @param n: an integer,denote the number of cities
     * @param roads: a list of three-tuples,denote the road between cities
     * @return: return the minimum cost to travel all cities
     */
    int minCost(int n, vector<vector<int>> &roads) {
        int len = roads.size();
        int inf = 1000000000;
        graph.resize(n + 1, vector<int>(n + 1, inf));
        constructGraph(roads, n);
        
        int state_size = (1 << n); //0000 - 1111
        
        //dp[i][j] : the min cost when state is i, the last passed city is j
        vector<vector<int>> dp(state_size, vector<int>(n + 1, inf));

        dp[1][1] = 0; // start from city 1, state is 0000001, minCost is 0
        
        for (int state = 0; state < state_size; ++state) {
            for (int i = 2; i <= n; ++i) {
                if ((state & (1 << (i - 1))) == 0) continue; //state为当前状态。若当前状态没有经历城市i,不考虑。这里i-1是因为index从0开始。
                
                int prev_state = state ^ (1 << (i - 1)); //这里城市i肯定为1,异或掉,则只有上一个城市的状态了。
                
                for (int j = 1; j <= n; ++j) {
                    if (i == j) continue;
                    dp[state][i] = min(dp[state][i], dp[prev_state][j] + graph[j][i]);
                }
            }
        }
        
        int min_cost = inf;
        for (int i = 1; i <= n; ++i) {
            min_cost = min(min_cost, dp[state_size - 1][i]);
        }
        
        return min_cost;
    }
    
private:
    vector<vector<int>> graph;
    void constructGraph(vector<vector<int>> & roads, int n) {
        int len = roads.size();
        for (int i = 0; i < len; ++i) {
            int source = roads[i][0];
            int destination = roads[i][1];
            int cost = roads[i][2];
        //    graph[source][destination] = cost;
        //    graph[destination][source] = cost;
            graph[source][destination] = min(graph[source][destination], cost);
            graph[destination][source] = min(graph[destination][source], cost);

        }
    }
};

解法2:优先队列式分支限界法 (branch and bound)。其实就是BFS。注意BFS 空间复杂度大。
详细介绍见 https://blog.csdn.net/u010089444/article/details/74331907

https://zhuanlan.zhihu.com/p/54131952

class Solution {
public:
    /**
     * @param n: an integer,denote the number of cities
     * @param roads: a list of three-tuples,denote the road between cities
     * @return: return the minimum cost to travel all cities
     */
    int minCost(int n, vector<vector<int>> &roads) {
        vector<vector<int>> cityLink(n, vector<int>(n, INT_MAX));
        vector<vector<int>> dp(n, vector<int>(1 << n, INT_MAX));
        
        for (auto & road: roads){
            int cityA = road[0] - 1, cityB = road[1] - 1, dist = road[2];
            cityLink[cityA][cityB] = cityLink[cityB][cityA] = min(cityLink[cityA][cityB], dist);
        }

        priority_queue<pair<int, pair<int,int>>> maxHeap; //<cost, <city index, state>>, sort by cost
        maxHeap.push({0, {0, 1}}); //init: the first city is visited
      
        dp[0][1] = 0; //cost of city 0 to itself is 0
        while (!maxHeap.empty()){
            auto now = maxHeap.top();
            maxHeap.pop();
    		int cost = now.first, city = now.second.first, state = now.second.second;
    		
    		for(int i = 0; i < n; ++i){
    		    if (cityLink[city][i] == INT_MAX) continue;
    		    if ((state & (1 << i)) == 0) { //if city i has not been visited
        			int new_state = (state | (1 << i)); //old state + city i
        			if(dp[i][new_state] > dp[city][state] + cityLink[city][i]) {
        				dp[i][new_state] = dp[city][state] + cityLink[city][i];
        				maxHeap.push({dp[i][new_state], {i, new_state}});
    			    }
    		    }
    		}
        }
        
        int minCost = dp[0][0];
    	for(int i = 0; i < n; i++) {
    		minCost = min(minCost, dp[i][(1 << n) - 1]);
    	}
    	
    	return minCost;
    }
};

解法3:DFS搜索

class Solution {
public:
    /**
     * @param n: an integer,denote the number of cities
     * @param roads: a list of three-tuples,denote the road between cities
     * @return: return the minimum cost to travel all cities
     */
    int minCost(int n, vector<vector<int>> &roads) {
        vector<vector<int>> cityLink(n, vector<int>(n, INT_MAX));
        for (auto & road: roads){
            int cityA = road[0] - 1, cityB = road[1] - 1, dist = road[2];
            cityLink[cityA][cityB] = cityLink[cityB][cityA] = min(cityLink[cityA][cityB], dist);
        }
        
        vector<bool> visited(n, false);
        vector<int> subset;
        
        subset.push_back(0); //city 0 
        visited[0] = true;
        
        dfs(cityLink, subset, visited, 0);
    	return minTravelCost;
    }

private:
    int minTravelCost = INT_MAX;
    void dfs(vector<vector<int>> & cityLink, vector<int> & subset, vector<bool> & visited, int cost) {
        int n = cityLink.size();
        if (subset.size() == n) {
            minTravelCost = min(minTravelCost, cost);
            return;
        }
        
        if (cost > minTravelCost) return;
        
        for (int i = 0; i < n; ++i) {
            int lastCity = subset.back();
            
            if (visited[i] || cityLink[lastCity][i] == INT_MAX) continue;
            
            subset.push_back(i);
            visited[i] = true;
            dfs(cityLink, subset, visited, cost + cityLink[lastCity][i]);
            visited[i] = false;
            subset.pop_back();
        }
    }
    
};

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值