旅行商問題(枚舉,回溯,動態規劃,貪心,分支界限)

文章目錄

  • 問題描述
  • 暴力枚舉
  • 回溯法
  • 動態規劃法
  • 貪心法
  • 分支界限法

問題描述

假設有一個貨郎擔要拜訪n個城市,他必須選擇所要走的路程,路程的限制時每個城市只能拜訪一次,而且最后要走到原來出發的城市,要求路徑長度。

在這里插入圖片描述

旅行商問題將要走過的城市建立成一個完全圖。稠密圖,所以用臨接矩陣來存。
由于路徑的特殊性,可以正走也可以反著走,所以一般存在兩條最優路徑同時也可以用這條性質檢驗算法的正確性。

暴力枚舉

使用dfs枚舉每一個點, 不適用剪枝的話就是暴力枚舉方法

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>using namespace std;const int N = 10;int g[N][N], n, m;
int cv = 0, bv = 0x3f3f3f3f;
bool st[N];vector<int> ans, x;void dfs(int k)
{if (k == n){// printf("before cv : %d\n", cv);// printf("last : {%d, %d} = %d\n", 1, x[k - 1], g[1][x[k - 1]]);cv += g[1][x[k - 1]];x.push_back(x[0]);for (auto i : x)printf("%d ", i);puts("");printf("{cv : %d}\n", cv);if(cv < bv){bv = cv;ans = x;}cv -= g[1][x[k - 1]];//注意最后一個加的cv要減掉x.pop_back();//同樣也要刪掉return;}for (int i = 1; i <= n; i ++){if (!st[i]){st[i] = true;x.push_back(i);//注意x的添加要在前面不然后面下標會出錯cv += g[x[k - 1]][i];// printf("{%d, %d} : %d\n", x[k - 1], i,  g[x[k - 1]][i]);dfs(k + 1);cv -= g[x[k - 1]][i];x.pop_back();st[i] = false;}}
}void out()
{puts("路徑為:");for (int i = 0; i <= n; i ++){printf("%d", ans[i]);printf(i == n ? "\n" : "->");}
}int main()
{memset(g, 0x3f, sizeof g);scanf("%d%d", &n, &m);for (int i = 0; i < m; i ++){int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = g[b][a] = min(g[a][b], c);}for (int i = 0; i <= n; i ++) g[i][i] = 0; st[1] = true; x.push_back(1);dfs (1);puts("最短路徑為:");printf("%d\n", bv);out();reverse(ans.begin(), ans.end());out();puts("");return 0;
}

在這里插入圖片描述

回溯法

回溯法就是在暴力枚舉的是后加上剪枝函數減少枚舉的結點數目
剪枝函數為
左剪枝:
當 c v > b v 時減去 當cv > bv時減去 cv>bv時減去
在這里插入圖片描述
在暴力枚舉的基礎上加上這個剪枝函數就行

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>using namespace std;const int N = 10;int g[N][N], n, m;
int cv = 0, bv = 0x3f3f3f3f;
bool st[N];vector<int> ans, x;void dfs(int k)
{if (k == n){// printf("before cv : %d\n", cv);// printf("last : {%d, %d} = %d\n", 1, x[k - 1], g[1][x[k - 1]]);cv += g[1][x[k - 1]];x.push_back(x[0]);for (auto i : x)printf("%d ", i);puts("");printf("{cv : %d}\n", cv);if(cv < bv){bv = cv;ans = x;}cv -= g[1][x[k - 1]];//注意最后一個加的cv要減掉x.pop_back();//同樣也要刪掉return;}for (int i = 1; i <= n; i ++){if (!st[i]){st[i] = true;x.push_back(i);cv += g[x[k - 1]][i];// printf("{%d, %d} : %d\n", x[k - 1], i,  g[x[k - 1]][i]);if (cv <= bv)dfs(k + 1);cv -= g[x[k - 1]][i];x.pop_back();st[i] = false;}}
}void out()
{puts("路徑為:");for (int i = 0; i <= n; i ++){printf("%d", ans[i]);printf(i == n ? "\n" : "->");}
}int main()
{memset(g, 0x3f, sizeof g);scanf("%d%d", &n, &m);for (int i = 0; i < m; i ++){int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = g[b][a] = min(g[a][b], c);}for (int i = 0; i <= n; i ++) g[i][i] = 0; st[1] = true; x.push_back(1);dfs (1);puts("最短路徑為:");printf("%d\n", bv);out();reverse(ans.begin(), ans.end());out();puts("");return 0;
}

搜索的結點數變成了
在這里插入圖片描述
相比窮舉減少了搜索的結點數

動態規劃法

狀態壓縮dp
利用一個int位中的32位0/1bit碼來表示圖走了哪些點,如果此位為1表示經過,0表示還未經過

類似題目AcWing 91. 最短Hamilton路徑

在這里插入圖片描述

/*由于要遍歷每一個點所以不能用最短路徑算法從一個已知點到另一個點只需要關注兩個狀態:1、終點是什么, 2、經過了哪些點而dp[i][j] 表示從0到終點j,經過了二進制狀態(每個點有走過和沒走兩個狀態)的點的路徑狀態計算:dp[i][j] <- dp[i - j][k](在已經經過的點中,去掉點j的方案取最小)
*/
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 21, M = 1 << N;
int dp[M][N];
int w[N][N];
int n;int main()
{scanf("%d", &n);for (int i = 0; i < n; i ++)for (int j = 0; j < n; j ++)scanf("%d", &w[i][j]);memset(dp, 0x3f, sizeof dp);dp[1][0] = 0;for (int i = 0; i < 1 << n; i ++)for (int j = 0; j < n; j ++)if (i >> j & 1)for (int k = 0; k < n; k ++ )if (i >> k & 1)dp[i][j] = min(dp[i][j], dp[i - (1 << j)][k] + w[j][k]);// 轉移到達第i個點時將第i個點的狀態要去掉就// 例如要將100101的第2個點去掉就需要 - 000100 = 100001printf("%d\n", dp[(1 << n) - 1][n - 1]);// 100000 - 000001 = 0111111 要將n - 1位全置位為1只需要用n為1后面為0減個位1即可return 0;
}

貪心法

貪心就是從起點開始每次走從這個點出發權重最小的邊
但是這個尋找局部最優解的過程找到的并不是全局最優解
思路和生成最小生成樹的思路一樣,由于是完全圖稠密圖,所以使用prim算法更好

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 510, INF = 0x3f3f3f3f;int g[N][N], dist[N];
bool st[N];
int n, m;
vector<int> ans;int prime()
{memset(dist, 0x3f, sizeof dist);int res = 0;// 最小生成樹中的權重之和for (int i = 0; i < n; i ++){int t = -1;// t表示集合外與集合相連的邊最小的結點for (int j = 1; j <= n; j ++)if (!st[j] && (t == -1 || dist[j] < dist[t]))// 集合外的,第一次直接賦值,值更小的t = j;st[t] = true;// 加入集合ans.push_back(t);if (i && dist[t] == INF) return INF;// 不是第一個節點且到集合的距離無窮,說明各個結點都不連通if (i) res += dist[t];for (int j = 1; j <= n; j ++)dist[j] = min (dist[j], g[t][j]);// 更新與集合相連的最小值}return res;
}void out()
{puts("路徑:");for (int i = 0; i <= n; i ++){printf("%d", ans[i]);printf(i == n ? "\n" : "->");}
}int main()
{cin >> n >> m;memset(g, 0x3f, sizeof g);for (int i = 0; i < m; i ++){int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = g[b][a] = min (g[a][b], c);// 無向圖要將兩個方向的邊都賦上權重}int res = prime();if (res == INF) puts("impossible");else printf("%d\n", res + g[ans[n - 1]][1]);ans.push_back(ans[0]);out();reverse(ans.begin(), ans.end());out();return 0;
}

在這里插入圖片描述

分支界限法

使用優先隊列形式
cc為當前代價,rc為剩余結點的最小出邊代價和
下界函數為: cc + rc
左剪枝:
當 c c > b c 時剪枝 當cc > bc時剪枝 cc>bc時剪枝
右剪枝:
當 c c + r c > b c 是剪枝 當cc + rc > bc是剪枝 cc+rc>bc是剪枝

歸結左右剪枝都可以用bound = cc + rc進行剪枝

剩余結點最小出邊代價和就是枚舉剩余每條結點對沒給結點只算最小的出邊

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>using namespace std;const int N = 16;
const int INF = 0x3f3f3f3f;int g[N][N], n, m, bc = INF;
vector<int> ans;struct Node {int idx, cost, bound;vector<int> path;bool st[N];bool operator<(const Node& other) const {return bound + cost > other.bound + other.cost;  // 按照 bound + cost 降序排序}
};int bound(const Node& x) {int minCost = 0;for (int i = 1; i <= n; ++i) {if (x.st[i]) {int m = INF;for (int j = 1; j <= n; ++j) {if (x.st[j]) {m = min(m, g[i][j]);}}minCost += m;}}return minCost;
}void bfs() {priority_queue<Node> heap;Node head = {1, 0, 0, {1}, {false}};head.st[1] = true;heap.push(head);while (heap.size()) {auto t = heap.top();heap.pop();if (t.idx == n) {int cc = t.cost + g[t.path[t.idx - 1]][1];for (auto i : t.path)printf("%d ", i);printf("%d", 1);puts("");if (cc < bc){bc = cc;ans = t.path;}continue;}for (int i = 1; i <= n; ++i) {if (!t.st[i]) {Node newNode = t;newNode.st[i] = true;newNode.path.push_back(i);newNode.cost += g[newNode.path[newNode.idx - 1]][i];newNode.idx++;newNode.bound = bound(newNode); if(newNode.bound < bc)//左右剪枝通用,因為是排列樹左右都要算下界函數heap.push(newNode);}}}
}void out()
{puts("路徑:");for (int i = 0; i <= n; i ++){printf("%d", ans[i]);printf(i == n ? "\n" : "->");}
}int main() {memset(g, 0x3f, sizeof g);scanf("%d%d", &n, &m);for (int i = 0; i < m; ++i) {int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = g[b][a] = min(g[a][b], c);}bfs();printf("%d\n", bc);ans.push_back(ans[0]);out();reverse(ans.begin(), ans.end());out();return 0;
}

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/165450.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/165450.shtml
英文地址,請注明出處:http://en.pswp.cn/news/165450.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

為銷售賦能:利用 Splashtop 增強遠程培訓技術

遠程銷售團隊這一概念在當今快節奏的商業環境中日益普遍。各公司正在計劃在不同地點靈活開展銷售業務&#xff0c;希望利用技術優勢縮小地域差距。但是&#xff0c;這種向遠程銷售的轉型面臨著重大挑戰&#xff0c;尤其在培訓和發展領域。培訓遠程銷售團隊需要采用創新方法&…

常見樹種(貴州省):012茶、花椒、八角、肉桂、杜仲、厚樸、枸杞、忍冬

摘要&#xff1a;本專欄樹種介紹圖片來源于PPBC中國植物圖像庫&#xff08;下附網址&#xff09;&#xff0c;本文整理僅做交流學習使用&#xff0c;同時便于查找&#xff0c;如有侵權請聯系刪除。 圖片網址&#xff1a;PPBC中國植物圖像庫——最大的植物分類圖片庫 一、茶 灌…

鴻蒙 ark ui 輪播圖實現教程

前言&#xff1a; 各位同學有段時間沒有見面 因為一直很忙所以就沒有去更新博客。最近有在學習這個鴻蒙的ark ui開發 因為鴻蒙不是發布了一個鴻蒙next的測試版本 明年會啟動純血鴻蒙應用 所以我就想提前給大家寫一些博客文章 效果圖 具體實現 我們在鴻蒙的ark ui 里面列表使…

土地利用數據技術服務

一、背景介紹 土地是人類賴以生存與發展的重要資源和物質保障&#xff0c;在“人口&#xff0d;資源&#xff0d;環境&#xff0d;發展&#xff08;PRED&#xff09;”復合系統 中&#xff0c;土地資源處于基礎地位。隨著現代社會人口的不斷增長以及工業化、城市化進程的加速&a…

Excel使用VLOOKUP查詢數據

VLOOKUP函數在百度百科中的解釋是&#xff1a; 解釋一下&#xff0c;函數需要4個參數&#xff1a; 參數1&#xff08;lookup_value&#xff09;&#xff1a;需要匹配的值參數2&#xff08;table_array&#xff09;&#xff1a;在哪個區域里進行匹配參數3&#xff08;col_index…

Dubbo3使用Zookeeper作為注冊中心的方案討論!詳解DubboAdmin與PrettyZoo來監控服務的優劣!

文章目錄 一&#xff1a;Dubbo注冊中心的基本使用 二&#xff1a;Zookeeper注冊中心的使用 1&#xff1a;依賴引入 2&#xff1a;實際開發 三&#xff1a;Zookeeper作為注冊中心的使用展示 1&#xff1a;啟動注冊Zookeeper服務 2&#xff1a;引入注冊中心 (一)&#xf…

Java 21增強對Emoji表情符號的處理了

現一個 Java 21 中有意思的東西&#xff01; 在java.Lang.Character類中增加了用于確定字符是否為 Emoji 表情符號的 API&#xff0c;主要包含下面六個新的靜態方法&#xff1a; public static boolean isEmoji(int codePoint) {return CharacterData.of(codePoint).isEmoji(…

操作系統 day13(RR、優先級調度)

RR&#xff08;時間片輪轉&#xff09; 響應時間&#xff1a;系統中有10個進程正在并發執行&#xff0c;如果時間片為1秒&#xff0c;則一個進程被響應可能需要等待9秒。也就是說&#xff0c;如果用戶在自己進程的時間片外通過鍵盤發出調試命令&#xff0c;可能需要等待9秒才能…

中斷方式的數據接收

中斷接收簡介 回顧之前的代碼 之前的代碼是 等待標志位RXNE位為1才有數據 進而讀取數據存放在變量c中 再根據c變量的數據是為0還是為1進而編寫燈亮滅的代碼 if語句 但這樣的代碼明顯不符合裸機多任務的編程模型 因為在while中為進程 進程執行的時間不能大于5ms 但是while&…

Qt/QML編程學習之心得:一個Qt工程的學習筆記(九)

1、.pro文件 加CONFIG += c++11,才可以使用Lamda表達式(一般用于connect的內嵌槽函數) 2、QWidget 這是Qt新增加的一個類,基類,窗口類,QMainWindow和QDialog都繼承與它。 3、Main函數 QApplication a應用程序對象,有且僅有一個 a.exec() 進行消息循環、阻塞 MyWi…

《圖解Java數據結構與算法:微課視頻版》簡介

本書系統、全面地介紹數據結構的基礎理論與算法設計&#xff0c;精選數據結構考研習題和各類典型例題進行講解&#xff0c;案例和課后習題豐富&#xff0c;突出對數據結構算法實踐能力的培養。本書算法均采用Java語言實現&#xff0c;示例代碼可直接上機運行。 本書配套資源豐…

Spring-jdbcTemplate-配置數據庫連接池,配置文件方式beans.xml

1、jdbc.properties jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql:///studb jdbc.userroot jdbc.pwd123456 2、beans.xml <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans&…

Python BDD 框架比較之 pytest-bdd vs behave

pytest-bdd和behave是 Python 的兩個流行的 BDD 測試框架&#xff0c;兩者都可以用來編寫用戶故事和可執行的測試用例&#xff0c; 具體選擇哪一個則需要根據實際的項目狀況來看。 先簡單看一下兩者的功能&#xff1a; pytest-bdd 基于pytest測試框架&#xff0c;可以與pytest…

港口大型設備狀態監測及預測性維護策略

在現代港口運營中&#xff0c;大型設備的正常運行對于保障港口作業的高效性至關重要。為了實現設備的可靠性和持續性&#xff0c;港口管理者需要采取一系列狀態監測和預測性維護策略。 推進自動化和智能化是提高港口大型設備狀態監測和維護管理效率的重要途徑。通過應用先進的…

【計算機網絡筆記】數據鏈路層概述

系列文章目錄 什么是計算機網絡&#xff1f; 什么是網絡協議&#xff1f; 計算機網絡的結構 數據交換之電路交換 數據交換之報文交換和分組交換 分組交換 vs 電路交換 計算機網絡性能&#xff08;1&#xff09;——速率、帶寬、延遲 計算機網絡性能&#xff08;2&#xff09;…

讀像火箭科學家一樣思考筆記07_探月思維

1. 挑戰“不可能”的科學與企業 1.1. 互聯網 1.1.1. 和電網一樣具有革命性&#xff0c;一旦你插上電源&#xff0c;就能讓自己的生活充滿活力 1.1.2. 互聯網的接入可以幫助人們擺脫貧困&#xff0c;拯救生命 1.1.3. 互聯網還可以提供與天氣相關的信息 1.2. 用廉價、可靠的…

Windows如何截取屏幕圖片以及動態圖

在制作PPT或是其他演示文稿或是說明文檔的時候&#xff0c; 常常需要截取網頁或是屏幕的截圖&#xff0c;在Windows中有多種方式可以實現截取屏幕。 Windows 截取屏幕圖片的方式 在Windows 中截取屏幕中某個區塊的方式有&#xff1a; 方式1. 最原始的方式&#xff1a; 點擊 …

C練習題_2

一、單項選擇題(本大題共20小題,每小題2分,共40分。在每小題給出的四個備選項中選出一個正確的答案&#xff0c;并將所選項前的字母填寫在答題紙的相應位置上。&#xff09; 以下敘述中錯誤的是&#xff08;) A.對于double類型數組&#xff0c;不可以直接用數組名對數組進行整…

機器學習與藥物篩選的心得體會

機器學習在藥物設計里面的應用可以說還是比較常見的&#xff0c;尤其是搞計算的都會或多或少的涉及到這塊。比如國內做這塊比較多的&#xff0c;浙江大學的侯廷軍教授&#xff0c;北京化工大學的閆愛霞教授&#xff0c;華東理工大學的幾個做模擬計算的老師&#xff0c;上海藥物…

Unity機器學習 ML-Agents第一個例子

上一節我們安裝了機器學習mlagents的開發環境&#xff0c;本節我們創建第一個例子&#xff0c;了解什么是機器學習。 我們的例子很簡單&#xff0c;就是讓機器人自主移動到目標位置&#xff0c;不能移動到地板范圍外。 首先我們來簡單的了解以下機器學習的過程。 機器學習的過…