实验一 分治法求众数问题(或…)
#include <iostream>
#include <vector>
#include <map>
using namespace std;
// 定义一个函数,用于查找给定数组的众数 分治法
int findMode(const vector<int>& nums, int left, int right) {
// 如果左边界等于右边界,说明只有一个元素,直接返回该元素作为众数
if (left == right) {
return nums[left];
}
// 计算中间位置
int mid = left + (right - left) / 2;
// 递归查找左半部分的众数
int leftMode = findMode(nums, left, mid);
// 递归查找右半部分的众数
int rightMode = findMode(nums, mid + 1, right);
// 创建一个映射count,用于统计每个元素出现的次数
map<int, int> count;
//通过遍历数组中的元素,将每个元素作为键,出现次数作为值,存储到映射中。
for (int i = left; i <= right; ++i) {
count[nums[i]]++;
}
// 获取左半部分众数的出现次数
int leftCount = count[leftMode];
// 获取右半部分众数的出现次数
int rightCount = count[rightMode];
// 根据出现次数比较左右两部分的众数,返回出现次数较多的那个
if (leftCount > rightCount) {
return leftMode;
}
else if (leftCount < rightCount) {
return rightMode;
}
else {
// 如果左右两部分众数出现次数相同,则返回较大的那个
return max(leftMode, rightMode);
}
}
int main() {
// 定义一个包含重复元素的数组
vector<int> nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 1, 1, 2, 2, 3, 3, 3, 3 };
// 调用findMode函数查找众数,并将结果存储在变量mode中
int mode = findMode(nums, 0, nums.size() - 1);
cout << "{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 1, 1, 2, 2, 3, 3, 3, 3 }的众数是: " << mode << endl;
return 0;
}
/*这段代码实现了一个名为findMode的函数,它接受一个整数类型的向量nums和两个整数类型的参数left和right,表示要查找的数组范围。
函数的目的是找到给定数组中的众数并返回。
首先,函数检查左边界是否等于右边界,如果是,则说明只有一个元素,直接返回该元素作为众数。
否则,函数会计算中间位置mid,然后递归地在左半部分和右半部分分别查找众数。
这是通过调用findMode函数实现的,传入左边界、中间位置加一和右边界作为参数。
接下来,函数创建一个映射count,用于统计每个元素出现的次数。
通过遍历数组中的元素,将每个元素作为键,出现次数作为值,存储到映射中。
然后,函数获取左半部分众数leftMode和右半部分众数rightMode的出现次数。
通过查询映射count,可以获取这两个众数的出现次数。
最后,函数根据出现次数比较左右两部分的众数。如果左半部分众数的出现次数大于右半部分众数的出现次数,则返回左半部分众数;
如果左半部分众数的出现次数小于右半部分众数的出现次数,则返回右半部分众数;
如果左右两部分众数出现次数相同,则返回较大的那个。这是通过使用条件语句和比较运算符来实现的。
在main函数中,定义了一个包含重复元素的数组nums,然后调用findMode函数来查找众数,并将结果存储在变量mode中。
最后,输出众数的值。*/
#include <iostream>
#include <vector>
using namespace std;
// 分治求解函数
int findElement(const vector<int>& arr, int left, int right, int i) {
if (left > right) {
return -1; // 未找到元素,返回-1
}
int mid = left + (right - left) / 2;
if (arr[mid] == i) {
return arr[mid]; // 找到元素,返回该元素
}
else if (arr[mid] > i) {
return findElement(arr, left, mid - 1, i); // 在左半部分继续查找
}
else {
return findElement(arr, mid + 1, right, i); // 在右半部分继续查找
}
}
int main() {
vector<int> arr = { 1, 3, 5, 7, 9 };
int i = 5;
int result = findElement(arr, 0, arr.size() - 1, i);
if (result != -1) {
cout << "元素 " << i << " 在有序序列中的位置为: " << result << endl;
}
else {
cout << "元素 " << i << " 不在有序序列中" << endl;
}
return 0;
}
实验二 动态规划法求单源最短路径问题
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
// 定义图的边结构,包含目标节点和权重
struct Edge
{
int dest, weight;
};
// 定义图的结构,包含节点数 V 和邻接列表 adjList
struct Graph
{
int V;
vector<vector<Edge>> adjList;
};
// 递归和动态规划求解最短路径
int dp(const Graph& graph, int src, int dest, vector<vector<int>>& memo) //vector<vector<int>>& memo:表示一个二维向量,用于存储已经计算过的子问题的解。这样可以避免重复计算,提高算法的效率。
{
if (src == dest)
{ // 如果源节点等于目标节点,说明已经到达目的地,距离为0
return 0;
}
// 如果已经计算过从src到dest的最短路径,直接返回memo中的结果
if (memo[src][dest] != -1)
{
return memo[src][dest];
}
int minDistance = INT_MAX; // 初始化最小距离为整数极限,用于初始化最短距离和判断是否存在路径。
// 遍历源节点的邻接列表
for (const Edge& edge : graph.adjList[src])
{
int nextNode = edge.dest; // 获取下一个节点
int weight = edge.weight; // 获取边的权重
// 递归计算从nextNode到dest的最短路径
int distance = dp(graph, nextNode, dest, memo);
// 更新最短路径
if (distance != INT_MAX)
{
minDistance = min(minDistance, weight + distance);
}
}
// 将计算结果保存在memo中
memo[src][dest] = minDistance;
return minDistance; // 返回最短路径
}
// 主函数
int main() {
Graph graph = { // 定义图的结构
10, // 节点数为10
{ // 邻接列表
{{1, 4}, {2, 2}, {3, 3}},
{{4, 9}, {5, 8}},
{{4, 6}, {5, 7}, {6, 8}},
{{5, 4}, {6, 7}},
{{7, 5}, {8, 6}},
{{7, 8}, {8, 6}},
{{7, 6}, {8, 5}},
{{9, 7}},
{{9, 3}},
{}
}
};
int src = 0, dest = 9; // 定义源节点和目标节点
// 初始化memo数组,创建了一个大小为 graph.V 行、graph.V 列的二维向量,并将所有元素初始化为 -1。
vector<vector<int>> memo(graph.V, vector<int>(graph.V, -1));
// 调用dp函数求解最短路径
int shortestPath = dp(graph, src, dest, memo);
// 输出最短路径
if (shortestPath == INT_MAX)
{
cout << "从源节点 " << src << " 到节点 " << dest << " 不存在路径。" << endl;
}
else
{
cout << "从源节点 " << src << " 到节点 " << dest << " 的最短距离为 " << shortestPath << endl;
}
return 0;
}
/*
首先,定义了两个结构体,Edge和Graph。Edge表示图的边,包含目标节点和权重;Graph表示图的结构,包含节点数V和邻接列表adjList。
然后,实现了一个名为dp的递归函数,用于求解从源节点到目标节点的最短路径。
该函数接受四个参数:图graph、源节点src、目标节点dest和一个二维向量memo。memo用于存储已经计算过的子问题的解,避免重复计算。
在dp函数中,首先判断源节点是否等于目标节点,如果是,则说明已经到达目的地,距离为0,直接返回0。
如果已经计算过从源节点到目标节点的最短路径,则直接从memo中返回结果。
接下来,初始化最小距离minDistance为整数极限INT_MAX,用于初始化最短距离和判断是否存在路径。
遍历源节点的邻接列表,对于每个邻接节点,获取下一个节点nextNode和边的权重weight。
递归调用dp函数,计算从nextNode到目标节点的最短路径distance。
如果distance不等于INT_MAX,说明存在从nextNode到目标节点的路径,更新最小距离minDistance为当前最小距离和weight+distance中的较小值。
将计算结果保存在memo中,以便后续使用。
最后,返回最小距离minDistance作为从源节点到目标节点的最短路径。
在主函数中,定义了一个图graph,包含了节点数V和邻接列表adjList。然后定义了源节点src和目标节点dest。
初始化memo数组,创建了一个大小为graph.V行、graph.V列的二维向量,并将所有元素初始化为-1。
调用dp函数求解最短路径,并将结果保存在shortestPath变量中。
根据shortestPath的值判断是否存在路径,如果等于INT_MAX,则输出不存在路径的信息;否则,输出最短路径的结果。
*/
实验三 贪心法求汽车加油问题
#include <iostream>
using namespace std;
int n = 30;//加满油后可行驶的距离
const int m = 10; //加油站个数
int A[m] = { 10,15,10,17,8,17,13,9,11,10 };//加油站距离(为i-1与i之间的距离)
int main() {
int s = n;//当前剩余油量可行驶距离
int flag[m] = { 0,0,0,0,0,0,0,0,0,0 };//记录加油站索引
int k = 0;//加油站个数记录
for (int i = 0; i < m; i++)
{
if (n < A[i])
{
cout << "no solution!" << endl;
break;
}
if (s - A[i] >= 0)
{
s -= A[i];
}
else
{
s = n - A[i];
flag[k++] = i - 1;
}
}
int a = 0;
cout << "the need stations are:" << endl;
while (flag[a] != 0)
cout << flag[a++] << ",";
return 0;
}
实验四 回溯法求0/1背包问题
#include<iostream>
#include<stack>//使用栈(后进先出)
using namespace std;
int maxValue(int w[], int v[], const unsigned& length, const unsigned& capacity)
{
stack<int> Bag;//首先构造出一个空的背包
int max = 0;//不同装入情况中背包中物品最大的价值
int weight = 0;//当前背包中物品的总重量
int value = 0;//当前背包中物品的总价值
int i;
for (i = 0; ; i++)
{
if (weight + w[i] <= capacity)//背包装入该物品后不会超重
{
Bag.push(i);//入栈
weight += w[i];
value += v[i];
}
if (i == length - 1)
{
//接下来将本次装包物品价值与当前最高的装包物品价值进行比较,保留较大的一个
if (max < value)
{
max = value;
}
//此时取出最后装进包里的一件物品并对其下一件物品进行考虑——回溯法核心
{
i = Bag.top();//取出上一件装入的东西(回溯的过程)
Bag.pop();
weight -= w[i];
value -= v[i];
if (i == length - 1)//特殊情况:如果最后装进包里的物品本来在就是编号最大的物品,那么它后面就没有其他物品了,循环必定终止
{
if (Bag.empty())break;//当所有物品都被从包中拿出来后,这是说明已经遍历完所有情况,查找结束(相当于解空间树中最右边的子树已经走完了)
i = Bag.top();//取出上一件装入的东西,这就是回溯的过程
Bag.pop();
weight -= w[i];
value -= v[i];
}
}
}
}
return max;
}
int main()
{
unsigned num, capacity;
cout << "please enter the number of items:";
cin >> num;
int* weights = new int[num];
int* values = new int[num];
cout << "please enter the weight of each item:";
for (unsigned i = 0; i < num; i++)
{
cin >> weights[i];
}
cout << "please enter the value of each item:";
for (unsigned i = 0; i < num; i++)
{
cin >> values[i];
}
cout << "please enter the capacity of the bag:";
cin >> capacity;
cout << "the optimal solution to this problem of Backtracking is:" << maxValue(weights, values, num, capacity);
return 0;
}