[算法导论] 邮递员问题代码c++实现,Floyd算法+dp,求遍历所有边回到起点的最短路径

Part1:Floyd

初始邻接矩阵:
[[0 6 13],
[10 0 4],
[5 ∞ 0]]

Floyd 算法的执行过程
A A(-1) A(0) A(1) A(2)
V0 V1 V2 V0 V1 V2 V0 V1 V2 V0 V1 V2
V0 0 6 13 0 6 13 0 6 10 0 6 10
V1 10 0 4 10 0 min(23,4) 10 0 4 9 0 4
V2 5 ∞ 0 5 11 0 5 11 0 5 11 0

c动态数组:

void fun( int **array) 

c静态数组:

void fun( int array[][D])
void Func(int array[3][10])
void fun( int (*array)[D])

函数内部两种访问数组的方式都可以:

array[i][j]
*( *(array+i) + j)

Floyd 函数

# include
using namespace std;
//遍历所有边回到起点的最短路径
class Solution{
     
private:
    //最短路径矩阵
    void Floyd(vector<vector<int>>&graph,int N){
     
        // N个顶点
        for(int k=1;k<=N;++k){
     
            for(int i=1;i<=N;++i){
     
                for(int j=1;j<=N;++j){
     
                    if(i==j){
     
                        continue;
                    }
                    // k与i和j都相连
                    if(graph[i][k]!=-1&&graph[k][j]!=-1){
     
                        graph[i][j]= graph[i][j]==-1 ? graph[i][k]+graph[k][j]:min(graph[i][j],graph[i][k]+graph[k][j]);
                    }
                }
            }
        }
    }

Part2: dp 求最短路径

最短路径
遍历所有的组合情况,求出最短的组合方式,
例如比较 d(2,8)+d(4,6),d(2,6)+d(4,8), d(2,4)+d(6,8), 然后取最小值即为最终的优化方案:d(2,8)+d(4,6)。

求解该步骤的时候,可以使用DFS来寻找最短路径方案,也可以利用DFS的思想,改为动态规划来实现,(状压dp)。
动态规划思路的代码相对没有DFS更符合人们的思路,答题思路是构造一个dp表,行值为1,列值表示该集合中所含的元素,

奇度数数组的所有组合: 2^ods(包括空组合)
例如:集合7表示所含元素在奇度数组中下标为{1, 2, 3}的集合,0、1、2、3、12、13、23、123
e.g.:假设现在得到的奇数度点的数组为:[2,4,6,8](实际上该数组在后续代码的实现上是[0,2,4,6,8])
一共有四个有效点,则dp数组应为1×16的规模,
dp[0] 表示一个点也没有的集合,空集自然距离也为0,
而更大集合j的最短距离值,应该由更小的集合i及不在i中的两个点x,y求得
e.g.:当求出集合3({2, 4})的最短距离时,记录在dp[3]中,
而集合15({2,4,6,8})的最短距离可以用dp[3] + d[6][8]来完成更新,
如果dp[3]+d[6][8] 同理,当求出集合5({2,6})的最短距离时,记录在dp[5]中,则可以用dp[5]+d[4][8]

public:
    int shortestPath (vector<vector<int>>& graph, vector<int>& dev, int N) {
     
        // 最短路径矩阵
        Floyd (graph, N);
        // 度数为奇数的点集odds
        vector<int> odds;
        // 在奇数度集合odds后添加0
        odds.push_back(0);
        for (int i=1; i<=N; ++i) {
     
            // 按位与 : n转换为二进制与1进行按位与运算 (返回奇数度)
            if (dev[i]&1) {
     
                //odds保存奇数度顶点
                odds.push_back(i);
            }
        }
        // 查看odds
        //for(int i=0;i
        //    cout << odds[i];
        //}
        // 奇数度顶点个数ods
        int ods = odds.size()-1;
        // 二进制1向左移位ods=2位,二进制100=4
        vector<int> dp((1<<ods), -1);

        //查看dp.size()=4
        //cout << dp.size() <

        dp[0] = 0;
        for (int i=0; i<(1<<ods); ++i) {
     
            int x = 1;
            // i= 0,1,2,3.  1&i
            while ((1<<(x-1)) & i) {
     
                // 如果i是奇数,x=2。
                ++x;
                //2,2,3
                //cout<
            }
            // i=0 x=1,y=2.
            for (int y=x+1; y<=ods; ++y) {
     
                // 10 & 0=0 不执行
                if ((1<<(y-1)) & i) {
     
                    continue;
                }
                // x-1=0,y-1=1
                // dp[ 0|1|2 ]/dp[3] = graph[1][3]
                // -12 2 3
                // cout << 0|1|2;
                // 三目运算符扩展(见博客)??::
                dp[i|(1<<(x-1))|(1<<(y-1))] =
                // x到y的路径存在且dp存在
                dp[i] != -1 && graph[odds[x]][odds[y]] != -1 ?
                // dp[3]=-1
                dp[i|(1<<(x-1))|(1<<(y-1))] == -1 ?
                // dp[3]不存在:dp[3]=dp[0]+graph[1][3]:
                dp[i]+graph[odds[x]][odds[y]] :
                    //dp[3]存在:dp[0]+graph[1][3]
                    min(dp[i|(1<<(x-1))|(1<<(y-1))], dp[i]+graph[odds[x]][odds[y]])
                    //x到y路径不存在:dp[3]
                    : dp[i|(1<<(x-1))|(1<<(y-1))];
            }
        }
        //2^ods(奇数度顶点个数)
        return dp[(1<<ods)-1];
    }

Part3: 主函数

主函数

int main()
{
     
    // N为顶点,R为边
    int N,R;
    cin >> N >> R;
    // graph初始化为-1,第一行和第一列是空出的。
    vector<vector<int>> graph(N+1,vector<int>(N+1,-1));
    // 度数,初始化为0
    vector<int> dev(N+1,0);
    int res=0;
    // routes:顶点1和2的距离为3,以此类推。
    vector<vector<int>> routes={
     {
     1,2,3},{
     2,3,4},{
     3,4,5},{
     1,4,10},{
     1,3,12}};
    // 由routes构造邻接矩阵graph
    for(int i=0;i<R;++i){
     
        int x=routes[i][0],y=routes[i][1],z=routes[i][2];
        graph[x][y]=z;
        graph[y][x]=z;
        //dev保存节点的度
        ++dev[x]; ++dev[y];
        // res是遍历路径长度,因为要遍历所有边并回到起点,所以res+=每条route
        res +=z;
    }
    // 所有边之和
    cout << res <<endl;
    //dev:0,3,2,3,2
    //for(int i=0;i<=N;i++){
     
    //    cout << dev[i];
    //}
    //cout<<" "<

    Solution solve;
    res += solve.shortestPath(graph,dev,N);
    // 遍历所有边回到起点的最短路径
    cout << res <<endl;

    3:不满足第一个条件
    //只满足第二个条件
    //int res_test1 = res==0  ? res==41 ? 1: 2: 3;
    //双条件都不满足
    //int res_test4 = res==0  ? res==1 ? 1: 2: 3;
    ///2 只满足第一个条件,不满足第二个条件
    //int res_test2 = res==41 ? res==0 ? 1: 2: 3;
    ///1 双条件都满足
    //int res_test3 = res==41 ? res!=0 ? 1: 2: 3;

    // 加括号,有优先级顺序
    //cout<< (res==41 ? 41 : 0) <
    //cout << res_test1 <
    //cout << res_test2 <
    //cout << res_test3 <
    //cout << res_test4 <

    //二进制运算 或
    //3 = 11(二进制)
    //cout<< (0|1|2) <
    //1 = 1
    //cout<< (0|1) <
    //1 = 1
    //cout<< (1|1) <
    return 0;
}

graph: 已求得的最短路径结果。
dev数组: 所有节点的度。
odds数组: 选择奇数度的节点组成。
dp数组: 初始化为-1。

参考:https://blog.csdn.net/qq_29592167/article/details/90246438
https://blog.csdn.net/junruitian/article/details/63253501

你可能感兴趣的:(算法导论)