首页 > 编程语言 >山东大学数据结构与算法课程设计实验4网络放大器设置问题

山东大学数据结构与算法课程设计实验4网络放大器设置问题

时间:2024-07-12 12:26:03浏览次数:20  
标签:10 课程设计 12 15 int 13 数据结构 山东大学 Pmax

问题描述

  一个汽油传送网络可由加权有向无环图G表示。图中有一个称为源点的顶点S。从S出发,汽油被输送到图中的其他顶点。S的入度为0,每一条边上的权给出了它所连接的两点间的距离。通过网络输送汽油时,压力的损失是所走距离的函数。为了保证网络的正常运转,在网络传输中必须保证最小压力Pmin。为了维持这个最小压力,可将压力放大器放在网络中的一些或全部顶点。压力放大器可将压力恢复至最大可允许的量级Pmax。令d为汽油在压力由Pmax降为Pmin时所走的距离。在设置信号放大器问题中,需要放置最少数量的放大器,以便在遇到一个放大器之前汽油所走的距离不超过d。编写一个程序来求解该问题。

基本要求

  针对网络设计问题考虑使用两种方法解决,并比较两种方法的时间性能,用图表显示比较结果。

数据结构与算法描述 

  输入Pmax和Pmin的值,还要节点n,以及m为边的个数,进行m个循环,以下m行输入边的起点、终点、每个边减少的石油量。然后对所有的节点进行拓扑排序。

bool topsort(int di[]){
    int hh = 0, tt = -1;
    for (int i = 1; i <= n; i++){
        if (di[i]==0) q[++tt] = i;
    }
    while (hh <= tt){
        int t = q[hh++];
        for (int i = h[t]; i != -1; i = ne[i]){
            int j = e[i];
            if (--di[j] == 0) q[++tt] = j;
        }
    }
    return tt == n - 1;
}

  方案一bfs贪心算法,用bfs进行遍历整个拓扑序列,遍历每个点的邻接点,判断是否存在压力小于Pmin的终点,如果存在将其起点放放大器,将该起点压力改为Pmax,并将压力值更新为原值和现在值中的较大值。如果不存在则只将邻接点的压力值更新为原值和现在值中的较大值。更新为较大值是为了贪心的保证放置的放大器最少。

void bfs(){//贪心
    int hh = 0;
    while (hh <= n - 1){
        //按拓扑序列遍历
        int t = q[hh];
        int flag = 0;
        int minp;
        //检查当前节点是否要放放大器
        for (int i = h[t]; i != -1; i = ne[i]){//遍历与当前节点邻接的点
            int j = e[i];
            minp = p[t] - w[i];
            //最小值检查是否需要放
            if (minp < Pmin){
                flag = 1;
                break;
            }
        }
        //所有与当前节点连接的结点的压力值更新
        if (flag == 0) {
            for (int i = h[t]; i != -1; i = ne[i]) {
                int j = e[i];
                p[j] = max(p[j], p[t] - w[i]);
            }
        }
        if (flag == 1) {
            ans++;
            p[t] = Pmax;
            booster[t] = true;
            for (int i = h[t]; i != -1; i = ne[i]) {
                int j = e[i];
                p[j] = max(p[j], p[t] - w[i]);
            }
        }
        hh++;
    }
}

  方案二用dfs递归遍历所有放不放放大器的情况,并适时剪枝。遍历所有情况用一个数组来记录是否放放大器,每次要分为放和不放两种情况,再接着调用递归函数。每次递归要遍历是否存在压力小于Pmin的情况,如果存在且在当前数组所在点之前出现小于的情况直接return(剪枝操作),如果是之后则继续递归。如果出现递归中此种情况统计的放大器个数大于之前的直接return(剪枝操作),或者统计完所有节点直接return。如果遍历中没有出现小于Pmin的情况则跟新放大器个数为当前统计和之前统计中较小的那个。

void dfs(int index, int cnt){
    int t = q[index];
    if (index >= n - 1){
        ans = min(ans, cnt);
        return;
    }
    int flag = 0;
    //判断当前点连接的所有点的压力值是否有小于最小压力值的
    for (int i = h[t]; i != -1; i = ne[i]) {
        int j = e[i];
        int tempp = p[t] - w[i];
        if (tempp < Pmin){
            flag = 1;
            break;
        }
     }
    //左分支:不放
    if (flag == 0) {
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            p[j] = max(p[j], p[t] - w[i]);
        }
        dfs(index + 1, cnt);
    }
    //右分支:放(第二次进循环)
    if (flag == 1) {
        cnt++;
        //剪枝
        if (cnt >= ans)
            return;
        else {
            p[t] = Pmax;
            booster[t] = true;
            for (int i = h[t]; i != -1; i = ne[i])
            {
                int j = e[i];
                p[j] = max(p[j], p[t] - w[i]);
            }
            dfs(index + 1, cnt);
        }
    }
}

  主函数中用函数得到计数器、以及系统时钟频率来计算时间的值,并将其baocunda哦文件中,并用一段代码将这些结果整理为markdown图标文件。

int main(){
    memset(h, -1, sizeof h);
    cin >> Pmax >> Pmin;
    cin >> n >> m;
    for (int i = 0; i < m; i++){
        int x, y, c;//起点、终点、距离
        cin >> x >> y >> c;
        add(x, y, c);
        d[y]++;//入度
    }
    for (int i = 1; i <= n; i++){
        p[i] = -0x3f3f3f3f;
        booster[i] = false;
    }
    p[1] = Pmax;
    if (topsort(d)) {
        cout << "拓扑排序成功\n";
    }
    else {
        cout << "拓扑排序不成功\n";
    }
    cout << "拓扑序为";
    for (int i = 0; i < n; i++){
        cout << q[i] << " ";
    }
    cout << endl;
    //方案1
    LARGE_INTEGER litmp1, litmp2;//用于存储系统时钟频率和初始计数值
    long long qt1, qt2;
    double dft, dff, dfm;
    QueryPerformanceFrequency(&litmp1);//获取系统时钟的频率,并将结果存储在 litmp1 变量中
    dff = (double)litmp1.QuadPart;//将 litmp1 中的时钟频率值转换为 double 类型,并将其赋值给变量 dff。
    QueryPerformanceCounter(&litmp1);//获取当前系统的计数值(初始值),并将结果存储在 litmp1 变量中
    qt1 = litmp1.QuadPart;//初始计数值
    bfs();
    cout << "方案一放大器的放置位置;";
    for (int i = 1; i <= n; i++) {
        if (booster[i]) cout << i << " ";
    }
    cout << endl;
    cout << "方案一得出的最少放大器个数为:" << ans << endl;
    QueryPerformanceCounter(&litmp1);//获得终止值
    qt2 = litmp1.QuadPart;//终止计数值
    dfm = (double)(qt2 - qt1);
    dft = dfm / dff;//获得对应的时间值
    ofstream fout1;
    fout1.open("scheme1time.txt", ios::app);//将方案一的时间值存入文件中
    fout1 << dft << endl;
    fout1.close();
    //方案2
    topsort(d);
    for (int i = 1; i <= n; i++){
        p[i] = -0x3f3f3f3f;
        booster[i] = false;
    }
    p[1] = Pmax;
    ans = INT_MAX;
    QueryPerformanceFrequency(&litmp2);//获得时钟频率
    dff = (double)litmp2.QuadPart;
    QueryPerformanceCounter(&litmp2);//获得初始值
    qt1 = litmp2.QuadPart;
    dfs(0, 0);
    cout << "方案二放大器的放置位置;";
    for (int i = 1; i <= n; i++){
        if (booster[i]) cout << i << " ";
    }
    cout << endl;
    cout << "方案二得出的最少放大器个数为:" << ans << endl;
    QueryPerformanceCounter(&litmp2);//获得终止值
    qt2 = litmp2.QuadPart;
    dfm = (double)(qt2 - qt1);
    dft = dfm / dff;//获得对应的时间值
    ofstream fout2;
    fout2.open("scheme2time.txt", ios::app);
    fout2 << dft << endl;
    fout2.close();
    return 0;
}
#include <iostream>
#include <fstream>
#include<vector>
using namespace std;
vector<double> a, b;

void read(vector<double>& array, string path){
    ifstream fin;
    fin.open(path);
    double temp;
    while (fin >> temp){
        array.push_back(temp);
    }
    fin.close();
}
int main(){
    read(a, "scheme1time.txt");
    read(b, "scheme2time.txt");
    ofstream fout("time.md");
    fout << "|scheme|example1|example2|example3|example4|" << endl;
    fout << "|--|--|--|--|--|" << endl;
    fout << "|1|" << a[0] << "|" << a[1] << "|" << a[2] << "|" << a[3] << "|" << endl;
    fout << "|2|" << b[0] << "|" << b[1] << "|" << b[2] << "|" << b[3] << "|" << endl;
    return 0;
}

测试结果 (以及提供样例)

  样例一和终端输出

  其他三个样例 

    样例二

51 2
8 12
1 2 50
1 3 40
1 6 43
2 5 41
2 6 22
2 4 1
4 6 11 
5 6 9
5 7 32
7 8 35
7 8 23
6 8 7

    样例三

30 3
16 21
1 2 28
1 3 25
1 4 5
2 7 9
2 6 15
3 5 10
4 5 20
4 6 30
5 7 10
5 8 26
6 9 7
7 13 23
8 10 5
8 11 1
9 12 10
9 14 9
11 12 2
10 13 6
13 16 4
11 16 19
14 15 13

    样例四 

94 3
15 90
1 2 59
1 3 33
1 12 58
1 10 6
1 2 48
1 9 68
1 10 28
1 2 67
1 9 83
1 5 64
1 6 67
1 10 39
2 4 13
2 5 5
2 10 48
2 9 85
2 8 29
2 14 49
2 6 32
2 15 76
3 11 49
3 5 41
3 15 67
3 14 92
3 10 77
3 5 19
3 5 32
4 6 21
4 15 28
4 5 52
4 8 92
4 13 53
4 7 5
4 5 87
5 11 78
5 12 76
5 13 43
5 14 2
5 7 26
5 13 89
5 6 90
5 14 58
5 7 77
5 14 52
5 12 17
5 12 40
5 11 14
5 11 66
5 6 29
5 6 27
5 6 88
6 7 10
6 13 65
6 13 88
6 7 38
6 9 65
6 7 33
6 14 67
7 8 31
7 9 55
7 9 62
7 10 90
7 15 85
7 9 71
7 10 50
8 13 88
8 15 94
8 12 84
8 12 17
8 15 59
9 10 41
9 10 68
9 13 26
9 12 29
9 15 25
9 15 40
9 13 13
9 12 16
10 12 22
10 13 16
10 12 47
10 15 32
10 12 47
11 12 2
12 15 94
12 13 57
12 13 59
12 13 26
13 14 84
14 15 45 

  两种方法的时间统计和表格 

 

完整代码和做图标代码

  统计时间的代码

#include<iostream>
#include<vector>
#include<fstream>
#include<string>
#include<queue>
#include<windows.h>
using namespace std;
typedef pair<int, int> pp;
const int N = 1000010;
int Pmax, Pmin;
int h[N];//i是结点编号,值是以i为起点第一条边的编号
int e[N];//i是边编号,值是i这条边终点的结点编号
int ne[N];//值是和i这条边同起点的下一条边的编号
int w[N];
int p[N];//点i的压力值
bool booster[N];//点i是否放放大器
int idx;
int d[N];//存储每个点的入度
int q[N];//存储拓扑序列
int st[N];//记录是否遍历过的状态
int dis[N];//距离
vector<pp> path;//记录最小生成树路径
int n, m;//边数、点数
int ans;//结果
void add(int a, int b, int c){
    e[idx] = b;//终点
    w[idx] = c;//减少的值
    ne[idx] = h[a];//同起点的其他点
    h[a] = idx++;
}
//拓扑排序
bool topsort(int di[]){
    int hh = 0, tt = -1;
    for (int i = 1; i <= n; i++){
        if (di[i]==0) q[++tt] = i;
    }
    while (hh <= tt){
        int t = q[hh++];
        for (int i = h[t]; i != -1; i = ne[i]){
            int j = e[i];
            if (--di[j] == 0) q[++tt] = j;
        }
    }
    return tt == n - 1;
}
void bfs(){//贪心
    int hh = 0;
    while (hh <= n - 1){
        //按拓扑序列遍历
        int t = q[hh];
        int flag = 0;
        int minp;
        //检查当前节点是否要放放大器
        for (int i = h[t]; i != -1; i = ne[i]){//遍历与当前节点邻接的点
            int j = e[i];
            minp = p[t] - w[i];
            //最小值检查是否需要放
            if (minp < Pmin){
                flag = 1;
                break;
            }
        }
        //所有与当前节点连接的结点的压力值更新
        if (flag == 0) {
            for (int i = h[t]; i != -1; i = ne[i]) {
                int j = e[i];
                p[j] = max(p[j], p[t] - w[i]);
            }
        }
        if (flag == 1) {
            ans++;
            p[t] = Pmax;
            booster[t] = true;
            for (int i = h[t]; i != -1; i = ne[i]) {
                int j = e[i];
                p[j] = max(p[j], p[t] - w[i]);
            }
        }
        hh++;
    }
}
void dfs(int index, int cnt){
    int t = q[index];
    if (index >= n - 1){
        ans = min(ans, cnt);
        return;
    }
    int flag = 0;
    //判断当前点连接的所有点的压力值是否有小于最小压力值的
    for (int i = h[t]; i != -1; i = ne[i]) {
        int j = e[i];
        int tempp = p[t] - w[i];
        if (tempp < Pmin){
            flag = 1;
            break;
        }
     }
    //左分支:不放
    if (flag == 0) {
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            p[j] = max(p[j], p[t] - w[i]);
        }
        dfs(index + 1, cnt);
    }
    //右分支:放(第二次进循环)
    if (flag == 1) {
        cnt++;
        //剪枝
        if (cnt >= ans)
            return;
        else {
            p[t] = Pmax;
            booster[t] = true;
            for (int i = h[t]; i != -1; i = ne[i])
            {
                int j = e[i];
                p[j] = max(p[j], p[t] - w[i]);
            }
            dfs(index + 1, cnt);
        }
    }
}
int main(){
    memset(h, -1, sizeof h);
    cin >> Pmax >> Pmin;
    cin >> n >> m;
    for (int i = 0; i < m; i++){
        int x, y, c;//起点、终点、距离
        cin >> x >> y >> c;
        add(x, y, c);
        d[y]++;//入度
    }
    for (int i = 1; i <= n; i++){
        p[i] = -0x3f3f3f3f;
        booster[i] = false;
    }
    p[1] = Pmax;
    if (topsort(d)) {
        cout << "拓扑排序成功\n";
    }
    else {
        cout << "拓扑排序不成功\n";
    }
    cout << "拓扑序为";
    for (int i = 0; i < n; i++){
        cout << q[i] << " ";
    }
    cout << endl;
    //方案1
    LARGE_INTEGER litmp1, litmp2;//用于存储系统时钟频率和初始计数值
    long long qt1, qt2;
    double dft, dff, dfm;
    QueryPerformanceFrequency(&litmp1);//获取系统时钟的频率,并将结果存储在 litmp1 变量中
    dff = (double)litmp1.QuadPart;//将 litmp1 中的时钟频率值转换为 double 类型,并将其赋值给变量 dff。
    QueryPerformanceCounter(&litmp1);//获取当前系统的计数值(初始值),并将结果存储在 litmp1 变量中
    qt1 = litmp1.QuadPart;//初始计数值
    bfs();
    cout << "方案一放大器的放置位置;";
    for (int i = 1; i <= n; i++) {
        if (booster[i]) cout << i << " ";
    }
    cout << endl;
    cout << "方案一得出的最少放大器个数为:" << ans << endl;
    QueryPerformanceCounter(&litmp1);//获得终止值
    qt2 = litmp1.QuadPart;//终止计数值
    dfm = (double)(qt2 - qt1);
    dft = dfm / dff;//获得对应的时间值
    ofstream fout1;
    fout1.open("scheme1time.txt", ios::app);//将方案一的时间值存入文件中
    fout1 << dft << endl;
    fout1.close();
    //方案2
    topsort(d);
    for (int i = 1; i <= n; i++){
        p[i] = -0x3f3f3f3f;
        booster[i] = false;
    }
    p[1] = Pmax;
    ans = INT_MAX;
    QueryPerformanceFrequency(&litmp2);//获得时钟频率
    dff = (double)litmp2.QuadPart;
    QueryPerformanceCounter(&litmp2);//获得初始值
    qt1 = litmp2.QuadPart;
    dfs(0, 0);
    cout << "方案二放大器的放置位置;";
    for (int i = 1; i <= n; i++){
        if (booster[i]) cout << i << " ";
    }
    cout << endl;
    cout << "方案二得出的最少放大器个数为:" << ans << endl;
    QueryPerformanceCounter(&litmp2);//获得终止值
    qt2 = litmp2.QuadPart;
    dfm = (double)(qt2 - qt1);
    dft = dfm / dff;//获得对应的时间值
    ofstream fout2;
    fout2.open("scheme2time.txt", ios::app);
    fout2 << dft << endl;
    fout2.close();
    return 0;
}

  做图标的代码 

#include <iostream>
#include <fstream>
#include<vector>
using namespace std;
vector<double> a, b;

void read(vector<double>& array, string path){
    ifstream fin;
    fin.open(path);
    double temp;
    while (fin >> temp){
        array.push_back(temp);
    }
    fin.close();
}
int main(){
    read(a, "scheme1time.txt");
    read(b, "scheme2time.txt");
    ofstream fout("time.md");
    fout << "|scheme|example1|example2|example3|example4|" << endl;
    fout << "|--|--|--|--|--|" << endl;
    fout << "|1|" << a[0] << "|" << a[1] << "|" << a[2] << "|" << a[3] << "|" << endl;
    fout << "|2|" << b[0] << "|" << b[1] << "|" << b[2] << "|" << b[3] << "|" << endl;
    return 0;
}

另一种做法代码参考 

class graph{
public:
    int idx = 0;
    int tp[N];//拓扑序列
    int in1[N];//记录点y的入度
    int p[N];
    bool s[N];//是否有放大器
    int tpn = 0;
    int ansnum;
    int n, m, Pmax;//点,边,权
    bool vis[N];//判断是否经过过这个点
    bool flag = true;
    int tnum;
    bool judge1(int tnum);
    graph(int n, int m, int Pmax){
        this->n = n;
        this->m = m;
        this->Pmax = Pmax;
        idx = 0;
    }
    void add(int x, int y, int z);
    void tpsort();
    void dfs1(int ss, int num);
};
void graph::add(int x, int y, int z){
    e[idx] = y;
    w[idx] = z;
    ne[idx] = h[x];
    h[x] = idx++;
    in1[y] ++;
}
//生成拓扑序列
void graph::tpsort(){
    tpn = 0;
    queue<int> q;
    for (int i = 1; i <= n; i++){
        if (in1[i] == 0)
            q.push(i);
    }
    while (q.size()) {
        auto t = q.front();
        q.pop();
        tp[++tpn] = t;
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            in1[j] --;
            if (in1[j] == 0)
                q.push(j);
        }
    }
}
bool graph::judge1(int tnum){
    flag = true;
    s[1] = true;
    memset(vis, false, sizeof vis);
    vis[1] = true;
    for (int i = 1; i <= n; i++) p[i] = Pmax;
    for (int i = 1; i <= n; i++){
        int t = tp[i];
        if (p[t] < Pmin){
            if (i <= tnum) flag = false;
            return false;
        }
        if (s[t]) p[t] = Pmax;//如果放了放大器
        for (int j = h[t]; j != -1; j = ne[j]){
            int u = e[j];
            if (!vis[u]){//没到过
                p[u] = p[t] - w[j];
                vis[u] = true;
            }
            else p[u] = min(p[u], p[t] - w[j]);
        }
    }
    for (int i = 1; i <= n; i++){
        int t = tp[i];
        if (p[t] < Pmin){
            if (t <= tnum) flag = false;
            return false;
        }
    }
    return true;
}
void graph::dfs1(int ss, int num){
    if (ss > this->n + 1 || num > ansnum)
        return;
    else if (judge1(ss - 1)){//完整的一次遍历
        ansnum = min(ansnum, num);
        return;
    }
    else if (flag == false){
        return;
    }
    s[tp[ss]] = true;
    dfs1(ss + 1, num + 1);
    s[tp[ss]] = false;
    dfs1(ss + 1, num);
}

如能打赏,不胜感激[叩谢]。

标签:10,课程设计,12,15,int,13,数据结构,山东大学,Pmax
From: https://blog.csdn.net/Water_Star1/article/details/140375144

相关文章

  • Redis数据结构—跳跃表 skiplist 实现源码分析
    Redis是一个开源的内存数据结构存储系统,它可以用作数据库、缓存和消息中间件。Redis的数据结构非常丰富,其中跳跃表(skiplist)是一种重要的数据结构,它被用来实现有序集合(sortedsets)。跳跃表是一种概率型数据结构,它通过多层链表来实现快速的查找操作。跳跃表的结构类似于多层索引,......
  • 数据结构笔记之表达式求值
    概念:运算是由运符和操作数组成的,DIY概念指的是左操作数、右操作数和运符之间的关系。中缀表达式:运符位于操作数之间,这是我们日常生活中最常用的表达式形式。后缀表达式:运符在操作数后面,这种表达式形式没有括号,易于解析。前缀表达式:运符在操作数前面,同样没有括号,但需要遵循不同......
  • Java 算法和数据结构 答案整理,最新面试题
    Java中如何使用动态规划求解背包问题?1、定义子问题:首先确定动态规划状态,通常以物品数量和背包容量为变量定义子问题,例如dp[i][j]表示前i件物品放入容量为j的背包所能获得的最大价值。2、确定状态转移方程:基于是否选择当前物品,将问题分为两个子问题,即dp[i][j]=......
  • 【C++】通讯录管理系统+少量数据结构
    #include<iostream>#include<string>usingnamespacestd;#definemax1000structnewp{ stringname; intsex; intage; stringnumber; stringadd;};structbooks{ structnewpa[max]; intsize;};staticvoidshowMenu(){ cou......
  • 数据结构复习计划之复杂度分析(时间、空间)
    第二节:算法时间复杂度和空间复杂度算法(Algorithm):是对特定问题求解方法(步骤)的一种描述,是指令的有限序列,其中每一条指令表示一个或多个操作。算法可以有三种表示形式: 伪代码 自然语言 流程图算法的五个特性①有穷性:一个算法必须总是在执行有穷步......
  • 【数据结构】双向链表
    Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎~~......
  • 数据结构(复杂度)
    复杂度算法在编写成可执行程序后,运⾏时需要耗费时间资源和空间(内存)资源。因此衡量⼀个算法的好坏,⼀般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。时间复杂度主要衡量⼀个算法的运⾏快慢,⽽空间复杂度主要衡量⼀个算法运⾏所需要的额外空间。在计算机发展的......
  • 嵌入式学习——C语言数据结构(三)
    七、赋值运算符    1、+=     加且赋值         C += A;等价于C=C+A    2、-=      减且赋值         C -= A;等价于C=C-A    3、*=      乘且赋值      ......
  • 操作系统课程设计-模拟文件管理系统java实现
    模拟文件管理系统学校的期末课程设计,进行一个简单的分享,不足之处请各位大佬指正。一、主要内容综合运用操作系统理论知识和编程知识设计实现一个可视化操作的文件管理模拟系统,该系统包含的基本信息:创建用户、登录用户、创建文件、删除文件、打开文件、显示文件、关闭文......
  • 数据结构(Java):集合类LinkedList&集合类Stack
    1、集合类LinkedList1.1什么是LinkedListLinkedList的底层是一个双向链表的结构(故不支持随机访问):在LinkedList中,定义了first和last,分别指向链表的首节点和尾结点。每个节点中有一个成员用来存储数据,还有两个指针域next和prev分别存储下一个节点和上一个节点的地址。Link......