算法设计第三次作业

算法设计第三次上机作业-求逆序对数 算法设计第三次上机作业-求逆序对数 题目描述 对于一个长度为N的整数序列A,满足i < j 且 Ai > Aj.的数对(i,j)称为整数序列A的一个逆序 请求出整数序列A的所有逆序对个数 输入 输入包含多组测试数据,每组测试数据有两行 第一行为整数N(1 <= N <= 20000),当输入0时结束 第二行为N个整数,表示长为N的整数序列 输出 每组数据对应一行,输出逆序对... 阅读详情

6.7.1.2

以下是使用C++语言描述矩阵连乘最优次序备忘录方法的代码:

#include <iostream>
#include <vector>
#include <climits>
using namespace std;

const int MAX_N = 100;
int dp[MAX_N][MAX_N];

// 计算矩阵i到j的最小乘次数
int matrixChainMultiplication(vector<int>& matrix, int i, int j) {
    if (i == j) {
        return 0;
    }
    if (dp[i][j] != -1) {
        return dp[i][j];
    }
    int res = INT_MAX;
    for (int k = i; k < j; k++) {
        int leftCost = matrixChainMultiplication(matrix, i, k);
        int rightCost = matrixChainMultiplication(matrix, k + 1, j);
        int currCost = matrix[i - 1] * matrix[k] * matrix[j];
        res = min(res, leftCost + rightCost + currCost);
    }
    dp[i][j] = res;
    return res;
}

int main() {
    int n = 5;
    vector<int> matrix = {10, 30, 5, 60, 20};
    memset(dp, -1, sizeof(dp));
    cout << "The minimum cost of matrix chain multiplication is: " << matrixChainMultiplication(matrix, 1, n - 1) << endl;
    return 0;
}
在上述代码中,矩阵的维度信息保存在向量matrix中,matrix[i]表示第i个矩阵的行数,同时也是第i-1个矩阵的列数。备忘录数组dp用于保存已经计算过的结果,避免重复计算,初始化为-1。

该方法的时间复杂度为O(n^3),其中n为矩阵数量。这是因为在计算矩阵i到j的最小乘次数时,需要枚举k,因此总共有n^2个子问题需要计算,每个子问题需要O(n)的时间复杂度来计算,因此总时间复杂度为O(n^3)。同时,由于使用了备忘录方法,避免了重复计算,因此空间复杂度也为O(n^2)。

6.7.1.3

以下是使用C++语言描述矩阵连乘最优次序动态规划算法的代码:

#include <iostream>
#include <vector>
#include <climits>
using namespace std;

const int MAX_N = 100;
int dp[MAX_N][MAX_N];

// 计算矩阵连乘的最小乘次数
int matrixChainMultiplication(vector<int>& matrix) {
    int n = matrix.size() - 1;
    for (int i = 1; i <= n; i++) {
        dp[i][i] = 0;
    }
    for (int len = 2; len <= n; len++) {
        for (int i = 1; i <= n - len + 1; i++) {
            int j = i + len - 1;
            dp[i][j] = INT_MAX;
            for (int k = i; k < j; k++) {
                int currCost = matrix[i - 1] * matrix[k] * matrix[j];
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + currCost);
            }
        }
    }
    return dp[1][n];
}

int main() {
    int n = 5;
    vector<int> matrix = {10, 30, 5, 60, 20};
    cout << "The minimum cost of matrix chain multiplication is: " << matrixChainMultiplication(matrix) << endl;
    return 0;
}
在上述代码中,矩阵的维度信息保存在向量matrix中,matrix[i]表示第i个矩阵的行数,同时也是第i-1个矩阵的列数。动态规划数组dp用于保存子问题的最优解,其中dp[i][j]表示从第i个矩阵到第j个矩阵的最小乘次数。

该方法的时间复杂度为O(n^3),其中n为矩阵数量。在计算矩阵连乘的最小乘次数时,需要枚举子问题的长度len、起点i和终点j,因此总共有n^3个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算,因此总时间复杂度为O(n^3)。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(n^2)。

Like
Dislike
Share

6.7.1.4

以下是使用C++语言描述任意顶点间最短路径动态规划算法的代码:

#include <iostream>
#include <vector>
#include <climits>
using namespace std;

const int MAX_V = 100;
int dp[MAX_V][MAX_V];

// 计算任意顶点i到j的最短路径
void floydWarshall(vector<vector<int>>& graph) {
    int V = graph.size();
    // 初始化dp数组
    for (int i = 0; i < V; i++) {
        for (int j = 0; j < V; j++) {
            dp[i][j] = graph[i][j];
        }
    }
    // 计算任意两点之间的最短路径
    for (int k = 0; k < V; k++) {
        for (int i = 0; i < V; i++) {
            for (int j = 0; j < V; j++) {
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
            }
        }
    }
}

int main() {
    int V = 4;
    vector<vector<int>> graph = {
        {0, 5, INT_MAX, 10},
        {INT_MAX, 0, 3, INT_MAX},
        {INT_MAX, INT_MAX, 0, 1},
        {INT_MAX, INT_MAX, INT_MAX, 0}
    };
    floydWarshall(graph);
    cout << "The shortest path between any two vertices is: " << endl;
    for (int i = 0; i < V; i++) {
        for (int j = 0; j < V; j++) {
            if (dp[i][j] == INT_MAX) {
                cout << "INF ";
            } else {
                cout << dp[i][j] << " ";
            }
        }
        cout << endl;
    }
    return 0;
}
在上述代码中,图的邻接矩阵保存在二维向量graph中,graph[i][j]表示从顶点i到顶点j的边的权重,如果没有边相连,则为INT_MAX。动态规划数组dp用于保存任意两点之间的最短路径,其中dp[i][j]表示从顶点i到顶点j的最短路径长度。

该方法的时间复杂度为O(V^3),其中V为图的顶点数量。在计算任意两点之间的最短路径时,需要枚举中间节点k、起点i和终点j,因此总共有V^3个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算,因此总时间复杂度为O(V^3)。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(V^2)。	

6.7.1.5

以下是使用C++语言描述多段图动态规划算法的代码:

#include <iostream>
#include <vector>
#include <climits>
using namespace std;

const int MAX_N = 100;
const int MAX_M = 100;
const int INF = INT_MAX;

// 计算多段图的最短路径
int multiStageGraph(vector<vector<int>>& graph, int n, int m) {
    int dp[MAX_N];
    // 初始化dp数组
    for (int i = 0; i <= n; i++) {
        dp[i] = INF;
    }
    // 最后一段到终点的距离为0
    dp[n] = 0;
    // 从后往前计算每个阶段的最短路径
    for (int i = n - 1; i >= 1; i--) {
        for (int j = 0; j < m; j++) {
            if (graph[i][j] != INF) {
                dp[i] = min(dp[i], graph[i][j] + dp[j + 1]);
            }
        }
    }
    return dp[1];
}

int main() {
    int n = 4, m = 3;
    vector<vector<int>> graph = {
        {INF, INF, INF, INF},
        {INF, INF, 2, 1},
        {INF, 5, INF, 4},
        {INF, INF, INF, INF},
        {INF, INF, INF, INF}
    };
    cout << "The shortest path in the multi-stage graph is: " << multiStageGraph(graph, n, m) << endl;
    return 0;
}
在上述代码中,多段图的邻接矩阵保存在二维向量graph中,graph[i][j]表示从第i段的顶点到第i+1段的顶点j的边的权重,如果没有边相连,则为INF。算法中使用一个一维数组dp来保存从当前阶段到终点的最短路径长度。

该方法的时间复杂度为O(nm),其中n为多段图的段数,m为每段中的顶点数。在计算每个阶段的最短路径时,需要枚举该阶段中的所有顶点,并在下一阶段中枚举所有可能的到达顶点,因此总共有nm个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算,因此总时间复杂度为O(nm)。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(n)。

6.7.2.1

以下是使用C++语言描述求任意两个有限序列的最长公共子序列动态规划算法的代码:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 计算最长公共子序列的长度
int longestCommonSubsequence(string s1, string s2) {
    int m = s1.size(), n = s2.size();
    vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (s1[i - 1] == s2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    return dp[m][n];
}

int main() {
    string s1 = "AGGTAB";
    string s2 = "GXTXAYB";
    cout << "The length of the longest common subsequence is: " << longestCommonSubsequence(s1, s2) << endl;
    return 0;
}
在上述代码中,字符串s1和s2为要比较的两个序列。算法中使用一个二维向量dp来保存计算结果,其中dp[i][j]表示s1的前i个字符和s2的前j个字符的最长公共子序列的长度。在计算dp[i][j]时,如果s1[i-1]等于s2[j-1],那么最长公共子序列中会包含s1[i-1]和s2[j-1],因此dp[i][j]等于dp[i-1][j-1]加上1;否则,最长公共子序列中不包含s1[i-1]和s2[j-1],因此dp[i][j]等于dp[i-1][j]和dp[i][j-1]中的较大值。

该方法的时间复杂度为O(mn),其中m和n分别为s1和s2的长度。在计算dp[i][j]时,需要枚举s1的前i个字符和s2的前j个字符,因此总共有mn个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(mn)。

6.7.2.2

以下是使用C++语言描述0/1背包问题动态规划算法的代码:

#include <iostream>
#include <vector>
#include <map>
using namespace std;

// 计算0/1背包问题的最大价值
int knapsack(vector<int>& w, vector<int>& v, int W) {
    int n = w.size();
    map<pair<int, int>, int> dp;
    for (int i = 0; i <= n; i++) {
        dp[make_pair(i, 0)] = 0;
    }
    for (int j = 1; j <= W; j++) {
        dp[make_pair(0, j)] = 0;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= W; j++) {
            if (w[i - 1] <= j) {
                dp[make_pair(i, j)] = max(dp[make_pair(i - 1, j)], dp[make_pair(i - 1, j - w[i - 1])] + v[i - 1]);
            } else {
                dp[make_pair(i, j)] = dp[make_pair(i - 1, j)];
            }
        }
    }
    return dp[make_pair(n, W)];
}

int main() {
    vector<int> w = {1, 2, 3};
    vector<int> v = {6, 10, 12};
    int W = 5;
    cout << "The maximum value of 0/1 knapsack problem is: " << knapsack(w, v, W) << endl;
    return 0;
}
在上述代码中,向量w和v分别表示物品的重量和价值,W表示背包的容量。算法中使用一个mapdp来保存计算结果,其中dp[make_pair(i, j)]表示前i个物品放入容量为j的背包中能够获得的最大价值。在计算dp[make_pair(i, j)]时,如果第i个物品的重量不超过j,那么可以选择将该物品放入背包中,此时dp[make_pair(i, j)]等于不放该物品的最大价值dp[make_pair(i-1, j)]和放该物品的最大价值dp[make_pair(i-1, j-w[i-1])] + v[i-1]中的较大值;否则,不能放该物品,此时dp[make_pair(i, j)]等于不放该物品的最大价值dp[make_pair(i-1, j)]。

该方法的时间复杂度为O(nW),其中n和W分别为物品的数量和背包的容量。在计算dp[make_pair(i, j)]时,需要枚举前i个物品和容量为j的背包,因此总共有nW个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(nW)。

6.7.2.3

以下是改写后的Floyd算法程序,计算有向图的自反传递闭包RTC的邻接矩阵:

#include <iostream>
#include <vector>
using namespace std;

void transitiveClosure(vector<vector<int>>& graph, vector<vector<int>>& rtc) {
    int n = graph.size();
    rtc = graph; // 初始化RTC矩阵为原图的邻接矩阵
    for(int k=0; k<n; k++) {
        for(int i=0; i<n; i++) {
            if(rtc[i][k] == 1) { // 如果rtc[i][k]为1,表示i到k有路径
                for(int j=0; j<n; j++) {
                    rtc[i][j] = rtc[i][j] || rtc[k][j]; // 更新rtc[i][j],如果rtc[k][j]为1,表示k到j有路径,则i到j也有路径
                }
            }
        }
    }
}

int main() {
    vector<vector<int>> graph = {
        {0, 1, 0, 0},
        {0, 0, 1, 1},
        {1, 0, 0, 0},
        {0, 0, 1, 0}
    };
    vector<vector<int>> rtc;
    transitiveClosure(graph, rtc);
    cout << "The RTC matrix is:" << endl;
    for(int i=0; i<rtc.size(); i++) {
        for(int j=0; j<rtc[i].size(); j++) {
            cout << rtc[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}
在上述代码中,邻接矩阵graph表示有向图的邻接矩阵,rtc表示自反传递闭包的邻接矩阵。初始时,将rtc矩阵赋值为graph矩阵,然后使用Floyd算法计算rtc矩阵。在Floyd算法的每一次迭代中,如果rtc[i][k]为1,表示i到k有路径,那么就遍历所有的j,如果rtc[k][j]为1,表示k到j有路径,则i到j也有路径,因此将rtc[i][j]更新为1。

该方法的时间复杂度为O(n^3),其中n为图的顶点数。在Floyd算法中,需要进行n次迭代,每次迭代需要遍历所有的i、j和k,因此总共需要进行O(n^3)次基本操作。空间复杂度为O(n^2),需要使用一个n×n的矩阵来保存rtc矩阵。

7.8.1

1
以下是使用递归方法生成含n个分量的所有排列的程序:

#include <iostream>
#include <vector>
using namespace std;

// 递归函数,生成所有排列
void generatePermutations(vector<int>& nums, int start, vector<vector<int>>& result) {
    if(start == nums.size() - 1) { // 当start指向最后一个元素时,生成一个排列
        result.push_back(nums);
        return;
    }
    // 从start到最后一个元素,分别与start交换位置,递归生成排列
    for(int i=start; i<nums.size(); i++) {
        swap(nums[start], nums[i]);
        generatePermutations(nums, start+1, result);
        swap(nums[start], nums[i]);
    }
}

// 生成所有排列的函数
vector<vector<int>> generatePermutations(int n) {
    vector<vector<int>> result;
    vector<int> nums(n);
    for(int i=0; i<n; i++) {
        nums[i] = i+1; // 初始化nums数组为1~n
    }
    generatePermutations(nums, 0, result);
    return result;
}

int main() {
    int n = 3;
    vector<vector<int>> permutations = generatePermutations(n);
    cout << "All permutations of " << n << " numbers are:" << endl;
    for(int i=0; i<permutations.size(); i++) {
        for(int j=0; j<permutations[i].size(); j++) {
            cout<< permutations[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}
在上述代码中,generatePermutations函数是递归函数,用于生成所有排列。参数nums表示待排列的数组,start表示当前处理的位置,result表示所有排列的结果。当start指向最后一个元素时,生成一个排列,将其加入到结果中。否则,从start到最后一个元素,分别与start交换位置,递归生成排列。在递归结束后,需要再将nums数组恢复为原来的顺序,以便下一次递归。

generatePermutations函数是生成所有排列的主函数。它首先初始化nums数组为1~n,然后调用generatePermutations函数生成所有排列。最后,将所有排列返回。

该方法的时间复杂度为O(n!),其中n为待排列的数组长度。在递归函数generatePermutations中,每次需要枚举从start到最后一个元素的所有元素,因此总共需要进行O(n!)次操作。空间复杂度为O(n^2),需要使用一个n长度的数组和一个n×n的矩阵来保存所有排列。
2
以下是使用递归方法生成n个元素的所有子集的程序:

#include <iostream>
#include <vector>
using namespace std;

// 递归函数,生成所有子集
void generateSubsets(vector<int>& nums, int start, vector<int>& subset, vector<vector<int>>& result) {
    result.push_back(subset); // 将当前子集加入到结果中
    // 从start到最后一个元素,分别加入到子集中,递归生成子集
    for(int i=start; i<nums.size(); i++) {
        subset.push_back(nums[i]);
        generateSubsets(nums, i+1, subset, result);
        subset.pop_back();
    }
}

// 生成所有子集的函数
vector<vector<int>> generateSubsets(int n) {
    vector<vector<int>> result;
    vector<int> nums(n);
    for(int i=0; i<n; i++) {
        nums[i] = i+1; // 初始化nums数组为1~n
    }
    vector<int> subset;
    generateSubsets(nums, 0, subset, result);
    return result;
}

int main() {
    int n = 3;
    vector<vector<int>> subsets = generateSubsets(n);
    cout << "All subsets of " << n << " numbers are:" << endl;
    for(int i=0; i<subsets.size(); i++) {
        for(int j=0; j<subsets[i].size(); j++) {
            cout<< subsets[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}
在上述代码中,generateSubsets函数是递归函数,用于生成所有子集。参数nums表示待生成子集的数组,start表示当前处理的位置,subset表示当前生成的子集,result表示所有子集的结果。每次将当前子集加入到结果中,然后从start到最后一个元素,分别加入到子集中,递归生成子集。在递归结束后,需要将最后一个加入的元素弹出,以便下一次递归。

generateSubsets函数是生成所有子集的主函数。它首先初始化nums数组为1~n,然后调用generateSubsets函数生成所有子集。最后,将所有子集返回。

该方法的时间复杂度为O(2^n),其中n为待生成子集的数组长度。在递归函数generateSubsets中,每次需要将当前子集加入到结果中,并枚举从start到最后一个元素的所有元素,因此总共需要进行O(2^n)次操作。空间复杂度为O(n^2),需要使用一个n长度的数组和一个n×n的矩阵来保存所有子集。
3
以下是一种Python语言描述的旅行商问题的回溯算法:

import sys

def tsp(graph, visited, curr_pos, n, count, cost, ans):
    # 如果已经遍历完所有城市,更新最优解
    if count == n:
        ans[0] = min(ans[0], cost + graph[curr_pos][0])
        return

    # 遍历所有未访问的城市
    for i in range(n):
        if visited[i] == False:
            visited[i] = True
            tsp(graph, visited, i, n, count+1, cost+graph[curr_pos][i], ans)
            visited[i] = False

# 旅行商问题的主函数
def solve_tsp(graph):
    n = len(graph)
    visited = [False] * n
    visited[0] = True
    ans = [sys.maxsize]
    tsp(graph, visited, 0, n, 1, 0, ans)
    print("The optimal cost is:", ans[0])

# 示例
graph = [[0, 10, 15, 20],
         [10, 0, 35, 25],
         [15, 35, 0, 30],
         [20, 25, 30, 0]]

solve_tsp(graph)
在上述代码中,tsp函数是旅行商问题的回溯函数。参数graph表示城市之间的距离矩阵,visited表示是否访问过某个城市,curr_pos表示当前所在的城市,n表示城市总数,count表示已经访问的城市数,cost表示当前路径的总花费,ans表示最优解。每次遍历所有未访问的城市,对于每个未访问的城市,先将其标记为已访问,然后递归调用tsp函数继续搜索,最后将其标记为未访问。如果已经遍历完所有城市,更新最优解。

solve_tsp函数是旅行商问题的主函数。它首先初始化visited数组,将起始城市标记为已访问,然后调用tsp函数求解最优解。

该算法的时间复杂度为O(n!),其中n为城市总数。在回溯函数tsp中,每次需要遍历所有未访问的城市,因此总共需要进行O(n!)次操作。空间复杂度为O(n),需要使用一个n长度的数组来保存visited数组。
4
以下是使用回溯方法求解子集和问题,并只输出一个满足条件的子集,或报告不存在满足条件的子集的Python代码:

def subset_sum(nums, target):
    n = len(nums)
    subset = []
    def backtrack(start, cur_sum):
        # 如果当前子集的和等于目标值,返回True
        if cur_sum == target:
            return True
        # 如果当前子集的和大于目标值,返回False
        if cur_sum > target:
            return False
        # 枚举从start到n-1的所有元素,依次加入到子集中
        for i in range(start, n):
            subset.append(nums[i])
            # 递归搜索,如果找到一个满足条件的子集,则返回True
            if backtrack(i+1, cur_sum+nums[i]):
                return True
            # 如果搜索失败,则弹出最后一个元素,继续搜索
            subset.pop()
        # 如果枚举完所有元素,仍然没有找到满足条件的子集,则返回False
        return False

    # 从0开始搜索
    if backtrack(0, 0):
        return subset
    else:
        return "No subset found."

# 示例
nums = [3, 1, 5, 9, 12]
target = 8
print(subset_sum(nums, target))
在上述代码中,subset_sum函数是子集和问题的回溯函数。参数nums表示待搜索的数组,target表示目标值,subset表示当前生成的子集。每次枚举从start到n-1的所有元素,依次加入到子集中。如果当前子集的和等于目标值,返回True;如果当前子集的和大于目标值,返回False。如果搜索失败,则弹出最后一个元素,继续搜索。如果枚举完所有元素,仍然没有找到满足条件的子集,则返回False。

该算法在最坏情况下的时间复杂度为O(2^n),其中n为待搜索的数组长度。在回溯函数subset_sum中,每次需要枚举从start到n-1的所有元素,因此总共需要进行O(2^n)次操作。空间复杂度为O(n),需要使用一个n长度的数组来保存当前子集。
5
以下是使用回溯方法判断一个无向图是否可m着色,并记录一种着色方案的Python代码:

def is_graph_colorable(graph, m):
    n = len(graph)
    colors = [-1] * n  # 初始将所有节点的颜色设置为-1,表示未着色

    def is_valid(node, color):
        for i in range(n):
            if graph[node][i] == 1 and colors[i] == color:
                return False
        return True

    def backtrack(node):
        # 如果所有节点都已经着色,返回True
        if node == n:
            return True
        # 枚举所有可行的颜色
        for color in range(m):
            # 如果当前颜色可行,着色并继续搜索下一个节点
            if is_valid(node, color):
                colors[node] = color
                if backtrack(node+1):
                    return True
                # 如果搜索失败,撤销当前着色
                colors[node] = -1
        # 如果所有颜色都失败,则返回False
        return False

    # 从第一个节点开始搜索
    if backtrack(0):
        return colors
    else:
        return "Graph is not m-colorable."

# 示例
graph = [[0, 1, 1, 1],
         [1, 0, 1, 0],
         [1, 1, 0, 1],
         [1, 0, 1, 0]]
m = 3
print(is_graph_colorable(graph, m))
在上述代码中,is_graph_colorable函数是判断一个无向图是否可m着色的回溯函数。参数graph表示待着色的无向图,m表示着色的颜色数,colors表示当前的着色方案。首先将所有节点的颜色设置为-1,表示未着色。然后枚举所有可行的颜色,如果当前颜色可行,着色并继续搜索下一个节点。如果所有节点都已经着色,返回True;如果搜索失败,撤销当前着色。如果所有颜色都失败,则返回False。

该算法的时间复杂度为O(m^n),其中n为无向图的节点数。在回溯函数is_graph_colorable中,每次需要枚举m个颜色,因此总共需要进行O(m^n)次操作。空间复杂度为O(n),需要使用一个n长度的数组来保存当前着色方案。

7.8.2

1
#include <iostream>
#include <vector>

using namespace std;

const int MAXN = 20; // 最大顶点数
int n; // 顶点数
int graph[MAXN][MAXN]; // 图
int path[MAXN]; // 路径
bool used[MAXN]; // 记录顶点是否被使用过

void dfs(int cur, int depth) {
    path[depth] = cur;
    used[cur] = true;

    if (depth == n - 1) { // 找到一条Hamilton回路
        if (graph[cur][0]) { // 如果最后一个顶点与起点相连,则输出路径
            for (int i = 0; i < n; i++) {
                cout << path[i] << " ";
            }
            cout << endl;
        }
    } else {
        for (int i = 0; i < n; i++) {
            if (!used[i] && graph[cur][i]) { // 如果顶点未被使用过且与当前顶点相连
                dfs(i, depth + 1);
            }
        }
    }

    used[cur] = false;
}

void findHamiltonCycles() {
    dfs(0, 0);
}

int main() {
    cin >> n;

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> graph[i][j];
        }
    }

    findHamiltonCycles();

    return 0;
}
时间复杂度分析:

最坏情况下,我们需要遍历所有的路径,因此时间复杂度为O(n!),其中n为顶点数。
2
#include <iostream>
#include <vector>

using namespace std;

const int MAXN = 20; // 最大顶点数
int n; // 顶点数
int graph[MAXN][MAXN]; // 图
int path[MAXN]; // 路径
bool used[MAXN]; // 记录顶点是否被使用过
bool found; // 是否找到Hamilton回路

void dfs(int cur, int depth) {
    path[depth] = cur;
    used[cur] = true;

    if (depth == n - 1) { // 找到一条Hamilton回路
        if (graph[cur][0]) { // 如果最后一个顶点与起点相连,则输出路径
            found = true;
            for (int i = 0; i < n; i++) {
                cout << path[i] << " ";
            }
            cout << endl;
        }
    } else {
        for (int i = 0; i < n; i++) {
            if (!used[i] && graph[cur][i]) { // 如果顶点未被使用过且与当前顶点相连
                dfs(i, depth + 1);
                if (found) {
                    return;
                }
            }
        }
    }

    used[cur] = false;
}

void findHamiltonCycle() {
    found = false;
    dfs(0, 0);
    if (!found) {
        cout << "不存在Hamilton回路" << endl;
    }
}

int main() {
    cin >> n;

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> graph[i][j];
        }
    }

    findHamiltonCycle();

    return 0;
}
时间复杂度分析:

由于我们只需要找到一条Hamilton回路或报告不存在Hamilton回路,因此在找到一条Hamilton回路后直接返回即可,时间复杂度为O(n!),其中n为顶点数。
3
#include <iostream>
#include <vector>

using namespace std;

int max_clique_size = 0;
vector<int> max_clique;

void max_clique(vector<vector<int>> &graph, vector<int> &clique, int pos) {
    if (pos == graph.size()) {
        if (clique.size() > max_clique_size) {
            max_clique_size = clique.size();
            max_clique = clique;
        }
        return;
    }
    bool can_add = true;
    for (int i = 0; i < clique.size(); i++) {
        if (graph[pos][clique[i]] == 0) {
            can_add = false;
            break;
        }
    }
    if (can_add) {
        clique.push_back(pos);
        max_clique(graph, clique, pos + 1);
        clique.pop_back();
    }
    if (graph.size() - pos > max_clique_size - clique.size()) {
        max_clique(graph, clique, pos + 1);
    }
}

vector<int> maximum_clique(vector<vector<int>> &graph) {
    max_clique_size = 0;
    vector<int> clique;
    max_clique(graph, clique, 0);
    return max_clique;
}
在上述代码中,maximum_clique函数是求解最大团问题的回溯函数。参数graph表示待搜索的无向图,max_clique_size表示当前最大的团的大小,max_clique表示当前最大的团。首先将max_clique_size初始化为0,max_clique初始化为空集。然后枚举所有可行的节点,如果当前节点能够与clique中所有节点相连,则将其添加到clique中,并继续搜索下一个节点。如果所有节点都已经添加到clique中,判断clique的大小是否大于当前最大的团的大小,如果是,则更新max_clique_size和max_clique。如果当前节点不可行或搜索失败,撤销当前添加的节点。如果剩余的节点数加上clique的大小仍然小于当前最大的团的大小,则不必继续搜索下去。

该算法的时间复杂度为O(2^n),其中n为无向图的节点数。在回溯函数maximum_clique中,每次需要枚举2个选择,因此总共需要进行O(2^n)次操作。空间复杂度为O(n),需要使用一个n长度的数组来保存当前搜索的团。
4
#include <iostream>
#include <vector>

using namespace std;

const int MAXN = 20; // 最大物品数
int n; // 物品数
int w[MAXN], v[MAXN]; // 物品的重量和价值
int c; // 背包容量
int ans; // 最大价值
int cur_w, cur_v; // 当前背包中物品的重量和价值
bool used[MAXN]; // 记录物品是否被使用过
vector<int> best_path, cur_path; // 最优解和当前解

void dfs(int depth) {
    if (depth == n) { // 搜索到叶子节点
        if (cur_v > ans) { // 更新最优解
            ans = cur_v;
            best_path = cur_path;
        }
        return;
    }

    // 不选当前物品
    dfs(depth + 1);

    // 选当前物品
    if (cur_w + w[depth] <= c) { // 如果不超过背包容量
        cur_w += w[depth];
        cur_v += v[depth];
        cur_path.push_back(depth);

        dfs(depth + 1);

        cur_path.pop_back();
        cur_v -= v[depth];
        cur_w -= w[depth];
    }
}

void knapsack() {
    dfs(0);

    cout << "最大价值为:" << ans << endl;
    cout << "最优解为:";
    for (int i = 0; i < best_path.size(); i++) {
        cout << best_path[i] << " ";
    }
    cout << endl;
}

int main() {
    cin >> n >> c;

    for (int i = 0; i < n; i++) {
        cin >> w[i] >> v[i];
    }

    knapsack();

    return 0;
}
时间复杂度分析:

最坏情况下,我们需要遍历所有的状态,因此时间复杂度为O(2^n),其中n为物品数。
5
#include <iostream>
#include <vector>

using namespace std;

const int MAXN = 20; // 最大元素个数
int n; // 元素个数
int a[MAXN]; // 元素值
int target; // 目标值
vector<int> cur_path; // 当前解

void dfs(int depth, int cur_sum) {
    if (depth == n) { // 搜索到叶子节点
        if (cur_sum == target) { // 如果当前和等于目标和,则输出当前解
            for (int i = 0; i < cur_path.size(); i++) {
                cout << cur_path[i] << " ";
            }
            cout << endl;
        }
        return;
    }

    // 不选当前元素
    dfs(depth + 1, cur_sum);

    // 选当前元素
    cur_path.push_back(a[depth]);
    dfs(depth + 1, cur_sum + a[depth]);
    cur_path.pop_back();
}

void subsetSum() {
    dfs(0, 0);
}

int main() {
    cin >> n >> target;

    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }

    subsetSum();

    return 0;
}
时间复杂度分析:

最坏情况下,我们需要遍历所有的状态,因此时间复杂度为O(2^n),其中n为元素个数。
6
#include <iostream>
#include <vector>

using namespace std;

const int MAXN = 20; // 最大顶点数
int n, m; // 顶点数和边数
int color[MAXN]; // 顶点的颜色
vector<int> adj[MAXN]; // 邻接表表示的图
int ans; // 最少需要的颜色数
vector<int> best_color; // 最优解

bool is_valid(int v, int c) {
    for (int i = 0; i < adj[v].size(); i++) { // 遍历v的所有邻居
        if (color[adj[v][i]] == c) { // 如果v的邻居已经使用了颜色c
            return false;
        }
    }
    return true;
}

void dfs(int depth) {
    if (depth == n) { // 搜索到叶子节点
        int cnt = 0; // 统计颜色数目
        for (int i = 0; i < n; i++) {
            cnt = max(cnt, color[i]);
        }
        if (cnt < ans) { // 更新最优解
            ans = cnt;
            best_color = vector<int>(color, color + n);
        }
        return;
    }

    for (int i = 1; i <= ans + 1; i++) { // 枚举所有可能的颜色
        if (is_valid(depth, i)) { // 如果颜色i可以使用
            color[depth] = i;
            dfs(depth + 1);
            color[depth] = 0;
        }
    }
}

void graphColoring() {
    dfs(0);

    cout << "最少需要的颜色数:" << ans << endl;
    cout << "最优解为:";
    for (int i = 0; i < best_color.size(); i++) {
        cout << best_color[i] << " ";
    }
    cout << endl;
}

int main() {
    cin >> n >> m;

    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    ans = n; // 初始时,最多需要n种颜色
    graphColoring();

    return 0;
}
时间复杂度分析:

最坏情况下,我们需要遍历所有的状态,因此时间复杂度为O(k^n),其中k为最大可能的颜色数。在实际运行中,k的值通常比较小,因此该算法的时间复杂度通常比较低。
多段图源码(C++版) 用C++写的多段图的源码,包括前路和后路 立即下载

相关推荐

动态规划解多段图问题

使用动态规划求解多段图问题的算法,C语言实现

【源代码】严蔚敏数据结构算法C++(十九)多段图

日常说明:有错误欢迎大家指正。另外本博客所有的代码博主编写后均调试通过。重要提醒!!!!博主使用的是VS2017,如果有低版本的小伙伴最好新建空项目将此代码复制上去。 更多算法请关注我的算法专栏https://blog.youkuaiyun.com/column/details/20417.html 有朋友反映让我多加备注,我觉得除非特别惊艳的方法需要说明外,这些基本的方法是容易明白的,当然前提是先搞懂算...

Handoking的博客【学无止境】 833

多段图的最短路径问题 动态规划法——C++代码

课程的随堂作业,C语言的,用dev就能运行,萌新代码,勿喷,仅仅帮助不想写作业的朋友方便一下,反正老师也不会仔细检查的

【动态规划】多部图问题代码

《计算机算法(C++版)》5.2. 用前向算法和后向算法进行动态规划,前向算法从后往前递推;后向算法,从前往后递推。 C++代码如下: #include #include #include using namespace std; class Graph { private: const static int MAXSIZE = 101; int VertexNum; in

Aegeaner的专栏 1423

多段图的最小路径求解c++实现

用动态规划实现求解多段图的最小路径,该代码分别实现从前和从后搜索

经典算法之多段图算法

经典算法之多段图算法,经典算法之多段图算法,经典算法之多段图算法!

多段图问题

求由s到t的最小成本路径 下面由5段图为例 找到1到12 的最短路径以及路径长度 #include <iostream> #include<fstream> #include <vector> #define MAX 9999 using namespace std; //初始化表 void read_file(vector<vector<int> >& g, vector<vector<int> >

豆豆豆豆芽的博客 525

Avellaneda & Stoikov’s market-making strategy 工程化处理

前2篇文章详细介绍了AS存货模型的做市策略,这次对于这些公式里面一些希腊字母的取值是如何计算的,如何做工程化并应用到生产环境中做一些说明。这个里面有一些重要参数:很直观,可以直接取值的有:s = current market mid price (中间价,也就是【最佳卖价+最佳买价】/2)q = quantity of assets in inventory of base asset (也就是买入多少价值的股票数量)

weixin_42480337的博客 7067

动态规划——多段图(简单实现)

最近学习算法分析,为了准备实验(迷糊了两节课),提前复(yu)习(xi)了一下算法书上讲的多段图问题;虽然花了一定时间,但还是有点收获,对动态规划有了更深的理解。现在把思考过程分享出来与君共勉。 首先贴出来参考书上的多段图(图片来源https://blog.youkuaiyun.com/ncepuzhuang/article/details/8923790) 有关书上算法流程分析什么的没来得及看,可以参考博...

qq_30836691的博客 3315

WPS占用C盘空间解决方案与优化技巧

办公软件在长期使用过程中会产生大量缓存和历史文件,这些文件往往隐藏在系统目录中,导致磁盘空间被无声占用。以WPS为例,其版本历史备份、云同步缓存和模板库是主要空间占用源。通过分析文件存储机制,可以定位到具体的缓存路径,如AppData目录下的Kingsoft文件夹。针对这一问题,有效的解决方案包括手动清理、自动化脚本和注册表优化。对于需要持续监控的场景,可以结合PowerShell脚本和任务计划实现自动化管理。在实际应用中,这些技术不仅适用于WPS,也可迁移到其他办公软件的空间优化中,是每位IT运维人员和高

weixin_30788239的博客 453

从Excel到Power Pivot:构建你的第一个商业智能分析模型

本文详细介绍了如何从Excel升级到Power Pivot,构建你的第一个商业智能分析模型。通过激活Power Pivot功能、导入数据、建立表关系、创建智能度量值以及打造交互式仪表盘,帮助用户高效处理百万行级别的数据,实现数据自动关联和动态分析,大幅提升商业决策效率。

weixin_30629653的博客 255

LeetCode【78. 子集】

LeetCode【78. 子集】

s_sos0的博客 346

OpenWRT软件源更换保姆级教程:从国内镜像到官方源一键切换(含常见报错解决)

本文提供OpenWRT软件源更换的详细教程,涵盖从国内镜像源到官方源的切换方法,并深度解析常见报错解决方案。针对国内网络环境,指导用户如何精准定位系统信息、备份配置、选择并配置清华等国内镜像源,以及处理签名验证失败、依赖缺失、404错误等典型问题,旨在提升软件包管理效率与稳定性。

weixin_29229261的博客 1931

算法分析与设计 第三次课外作业

TOC]

没事发发博客 579

【Unity-UGUI控件全面解析】| Layout自动布局组件详解

UGUI中有个自动布局组件Auto Layout,主要包含下面几个模块(点击可进入官方文档下查看描述): 自动布局 布局元素 (Layout Element) 水平布局组 (Horizontal Layout Group) 垂直布局组 (Vertical Layout Group) 网格布局组 (Grid Layout Group) 内容大小适配器 (Content Size Fitter) 自动布局系统 基于布局元素和布局控制器的概念。一个布局元素是具有矩形变换以及其他可选组件的游戏对象。布局元素对自身

努力前行,总会成为自己心中的那道光 2万+

VMware16虚拟机:安装Windows10系统---超详细教程

VMware16虚拟机:安装Windows10系统---超详细教程

MQ0522的博客 3万+

【国科大算法设计与分析】第三次pdf作业

​ 从左到右遍历 containers 数组(索引范围是 0 到 n - 2,因为只需要考虑往相邻的右侧容器移动物品的情况),对于每个容器 i:检查 diff_array[i](也就是当前容器物品数量与平均数的差值)是否大于 0,如果大于 0,说明该容器的物品数量多于平均数,需要往相邻容器移动物品。​ 由此可以总结出一个贪心的思路:在给定的整数中,从左到右依次比较相邻的数字,如果前面的数字比后面的数字大,那么优先删除前面较大的这个数字,这样能让高位上的数字尽可能小,从而使得最终组成的新整数最小。

踏上取经路,比抵达灵山更重要🐱‍👤 563

YOLO格式数据集【数据增强】终极脚本 | 70+种顶级数据增强方法 | 可带标签增强

YOLO格式数据集【数据增强】终极脚本 | 70+种顶级数据增强方法 | 可带标签增强

Coding... 5498

【国科大算法设计与分析】第三次oj作业

【代码】【国科大算法设计与分析】第三次oj作业。

踏上取经路,比抵达灵山更重要🐱‍👤 344

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

算法设计与分析第三次作业

#leetcode 685.Redundant Connection II In this problem, a rooted tree is a directed graph such that, there is exactly one node (the root) for which all other nodes are descendants of this node, plus e...

xiaoshang3636的博客 393

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

算法设计第三次上机作业-Raid

算法设计第三次上机作业-Raid 题目描述 有两种类型的点,一种是station,一种是agent,计算station和agent的最小距离。 输入 第一行是T,代表样例个数 之后的每一种情况,第一行是N,代表有N个station和N个agent。 接下来的2N行,依次是station和agent的坐标。 输出 每一行都输出最小距离,保留到小数点后三位。 解题思路 和求取平面内最小距离点对的解法一...

mimeidongyu的博客 356

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

算法设计与分析第三次作业第一题

实验报告 课程名称 《算法分析与设计》 实验日期 2021年 3 月 22 日 至 2021 年 3 月 25 日 学生姓名 郑超逸 所在班级 计算机195 学号 2019211909055 实验名称 检索算法寻找下标(二分查找) 实验地点 勤园13-208 同组人员 无 1.问题 在一个排好序的数组T[1…n]中查找x

zcy2019211909055的博客 134

算法设计与分析第三次作业第二题

实验报告 课程名称 《算法分析与设计》 实验日期 2021年 3 月 22 日 至 2021 年 3 月 25 日 学生姓名 郑超逸 所在班级 计算机195 学号 2019211909055 实验名称 检索算法寻找下标(顺序查找) 实验地点 勤园13-208 同组人员 无 1.问题 在一个排好序的数组T[1…n]中查找x

zcy2019211909055的博客 195

算法设计与分析第三次课后作业

分治排序

weixin_52670953的博客 262

算法设计第三次上机作业-Multiplication Puzzle

算法设计第三次上机作业-Multiplication Puzzle 题目描述 给出一排写有数字的卡片,每次都从这排卡片中抽取一张拿出(不能拿第一张和最后一张),将该卡片上的数字和其旁边的两个数字进行相乘,重复该过程直到最后只剩下第一张卡片和最后一张卡片。使得乘数相加得到的和最小。 例如,卡片上的数字为10 1 50 20 5, 如果抽取顺序为 1,20,50,那么最终得到的结果为 10150 + ...

mimeidongyu的博客 289
上一篇: net.coobird.thumbnailator.Thumbnails 库的使用
下一篇: 算法设计与分析第四次作业
jakeonil
博客等级 码龄5年 1万+粉丝 60原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jakeonil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值