贪心法实验报告
实验内容
-
单源最短路径问题,并对算法进行时间复杂性分析。
-
实现多机调度问题,并对算法进行时间复杂性分析。
【注】均以表格形式显示结果,再用文字说明问题的解并给出解的标准形式
实验目的
-
掌握贪心算法求解问题的一般特征和步骤;
-
使用贪心算法编程,求解单源最短路径问题和多机调度问题。
程序清单
/*
* @Description: 单源最短路问题的贪心算法
* @version:
* @Author:
* @Date: 2021-05-20 20:00:53
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-05-20 20:30:35
*/
// 迪杰斯特拉算法
// ! 可能有重复边,需要存两个结点间最短的的那个。
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#include <cstring>
#define MAX 1 << 30
int map[2001][2001];
using namespace std;
int main()
{
int num_node, num_edge;
cout << "Please input the number of node and edge:\n"
<< endl;
cin >> num_edge >> num_node;
// 初始化矩阵
for (int i = 0; i <= num_node; i++)
{
for (int j = 0; j <= num_node; j++)
map[i][j] = MAX;
}
// 输入边和权值
for (int i = 0; i < num_edge; i++)
{
int start, end, length;
cin >> start >> end >> length;
if (map[start][end] > length)
map[start][end] = length;
}
// visit数组标记该结点是否已经进地杰斯特拉算法的已判断的集合,初始化为false
vector<bool> visit(num_node + 1, false);
// distance数组记录起点到该结点的最短距离为多少,初始化为起点到各点的距离
vector<int> distance(num_node + 1, MAX);
for (int i = 0; i <= num_node; i++)
distance[i] = map[1][i];
// 将起点标记为true, 起点到起点的距离为0
visit[1] = true;
distance[1] = 0;
cout << "\n-------------------------" << endl;
cout << "1th" << endl;
for (int i = 1; i <= num_node; i++)
{
if (!visit[i] && distance[i] == MAX)
cout << i << "\tMAX" << endl;
else if (!visit[i] && distance[i] != MAX)
cout << i << "\t" << distance[i] << endl;
}
int count = 2;
for (int i = 1; i <= num_node; i++)
{
// 记录当前距离最小的结点是哪个,最短距离为多少
int min_node = 0, mindis = MAX;
// 找到当前未标记的距离最小结点
for (int j = 1; j <= num_node; j++)
if (!visit[j] && distance[j] < mindis)
{
mindis = distance[j];
min_node = j;
}
if (min_node == 0)
continue;
// 把该结点标记为已进入集合
visit[min_node] = true;
// 更新未进入集合的结点的距离数组,为下一轮的选择最小点做准备
for (int j = 1; j <= num_node; j++)
{
if (!visit[j] && distance[j] > distance[min_node] + map[min_node][j])
distance[j] = distance[min_node] + map[min_node][j];
}
cout << "\n-------------------------" << endl;
cout << count << "th" << endl;
count++;
for (int i = 1; i <= num_node; i++)
{
if (!visit[i] && distance[i] == MAX)
cout << i << "\tMAX" << endl;
else if (!visit[i] && distance[i] != MAX)
cout << i << "\t" << distance[i] << endl;
}
}
cout << "So far, all the nodes are selected into the set\n"
"The distance from the source point to the end point is:"
<< endl;
cout << distance[num_node] << endl;
system("pause");
return 0;
}
/*
9 6
1 2 1
1 3 12
2 3 9
2 4 3
4 3 4
3 5 5
4 5 13
4 6 15
5 6 4
*/
// 时间复杂度为O(n^2)
/*
* @Description: 多机调度问题
* @version:
* @Author:
* @Date: 2021-05-20 18:22:21
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-05-20 19:09:07
*/
// 即多个需要执行时间不同的作业,在若干机器上运行,求所需的最小时间
// t[]存储处理时间,且从大到小排序
// d[]存储m台机器的空闲时间
// S[i]以队列的形式存储机器i的处理作业
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
bool cmp(const pair<int, int> &a, const pair<int, int> &b)
{
return a.second > b.second;
}
void MultiMachine(vector<pair<int, int>> &t, int m)
{
sort(t.begin(), t.end(), cmp);
// 存储各个机器处理的作业序号
vector<vector<int>> S(m);
// 存储各机器的空闲时间
vector<int> d(m, 0);
// 记录作业数
int n = t.size();
int i, j, k;
// 安排前m个作业
for (i = 0; i < m && i < n; ++i)
{
S[i].push_back(t[i].first);
d[i] = t[i].second;
}
// 依次安排后续作业
for (i = m; i < n; ++i)
{
// 找出最先空闲的机器,k用来遍历,j保存当前最早
for (j = 0, k = 1; k < m; ++k)
if (d[k] < d[j])
j = k;
// 将当前作业插入到找出的最先空闲的机器队列中
S[j].push_back(t[i].first);
// 空闲时间更新为+=当前作业的处理时间
d[j] += t[i].second;
}
for (int i = 0; i < m; ++i)
{
cout << i + 1 << "th machine's task(s):" << ends;
for (auto it = S[i].cbegin(); it != S[i].cend(); ++it)
cout << "task" << *it << "\t";
cout << endl;
}
cout << "\nThe time required is " << ends;
int max = d[0];
for (int i = 1; i < m; ++i)
if (d[i] > max)
max = d[i];
cout << max << endl;
}
int main(void)
{
int num_task, num_machine;
cout << "Please input the number of task and machine:\n"
<< endl;
cin >> num_task >> num_machine;
vector<pair<int, int>> t;
for (int i = 0; i < num_task; ++i)
{
int time;
cin >> time;
t.push_back(make_pair(i + 1, time));
}
cout << endl;
MultiMachine(t, num_machine);
system("pause");
return 0;
}
/*
7 3
2 14 4 16 6 5 3
*/
// 该算法
// 排序的时间复杂度为O(nlogn)
// 完成前m个任务分配是O(m)
// 完成剩余任务的分配时间复杂度是O(n*m)
运行结果
分析与思考
- 第一个问题调试过程中出现的问题是,考虑到可能在抽象的图中不存在一对结点中存在多条边的情况,但是在实际问题中可能大量存在,所以采取了一种策略,即两点之间只存取最短的那条边的权值
- 第二个问题开始没有保存任务的最初编号,随后便使用pair组成键值对,保存原编号