日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

贪心算法简单实践 -- 分糖果、钱币找零、最多区间覆盖、哈夫曼编解码

發布時間:2023/11/27 生活经验 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 贪心算法简单实践 -- 分糖果、钱币找零、最多区间覆盖、哈夫曼编解码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 貪心算法概覽

貪心算法是一種算法思想。希望能夠滿足限制的情況下將期望值最大化。比如:Huffman編碼,Dijkstra單源最短路徑問題,Kruskal最小生成樹 等問題都希望滿足限制的情況下用最少的代價找到解決問題的辦法。

這個辦法就是貪心算法的思想。
實際用貪心算法解決問題可以有如下幾步:

  1. 當看到這類問題時 我們能夠聯想到貪心算法:針對一組數據,我們定義了期望值和限制值,希望在滿足限制值的情況下期望值最大。
    比如最短路徑中,限制值是每一次移動只能向下或向右,期望值是走到終點(某個頂點),期望用最少的步數走到目標頂點。
  2. 嘗試使用貪心算法來解決問題:每一次做出選擇時在對限制值同等貢獻量的情況下,選擇對期望值貢獻最大的數據。
  3. 嘗試在案例數據中舉例,查看貪心方式的實驗結果是否最優。

貪心算法適用的案例 是 每一次的選擇都是獨立事件,不會受到之前選擇的影響。比如有權圖中的最短路徑的查找,當前的選擇會對后續的選擇造成影響,第一次選擇最優的,后續可選的權值都是特別大的,則期望值并不是最優的。

可以看看如下幾個簡單實踐。

2. 貪心算法實踐

2.1 分糖果

我們有m個糖果和n個孩子。我們現在要把糖果分給這些孩子吃,但是糖果少,孩子多(m<n),所以糖果只能分配給一部分孩子。

每個糖果的大小不等,這m個糖果的大小分別是s1,s2,s3,…,sm。除此之外,每個孩子對糖果大小的需求也是不一樣的,只有糖果的大小大于等于孩子的對 糖果大小的需求的時候,孩子才得到滿足。假設這n個孩子對糖果大小的需求分別是g1,g2,g3,…,gn。如何分配糖果,能盡可能滿足最多數量的孩子?

期望值:最多滿足孩子的個數
限制值:糖果大小s >= g ,才視為該糖果滿足該孩子。
滿足限制值的情況下,不論用小糖果還是大糖果滿足孩子,對期望值的貢獻都是一樣的,滿足的孩子個數++ 而已。

貪心算法:

  • 對于一個孩子,用較小的糖果能夠滿足,則沒有必要用更大的糖果;更大的糖果可以用來滿足更多需求的孩子。
  • 對于一個糖果,從較小需求的孩子開始,越容易滿足。

代碼如下:

// simple greedy algorithm: distribute candies
// six childs' request: 1 1 3 2 2 8
// five candies size:   4 2 5 7 9
//
// Greedy algorithm is suitable to solve the problem.
// We can let the smaller candy's size to satisfy the 
// smaller resquest of child.
int distributeCandy(vector<int> candies,vector<int> childs) {if(candies.size() == 0 || childs.size() == 0) {return 0;}// sort, we can compare from small to big requestsort(candies.begin(), candies.end());sort(childs.begin(), childs.end());int res = 0;int i, j;for (i = 0, j = 0;i < childs.size() && j < candies.size(); i++,j++) {if (childs[i] <= candies[j]) {res ++;}}return res;
}

完整測試代碼:
https://github.com/BaronStack/DATA_STRUCTURE/blob/master/greedy/distribute_candies.cc

2.2 錢幣找零

這個問題在我們的日常生活中更加普遍。假設我們有1元、2元、5元、10元、20元、50元、100元這些面額的紙幣,它們的張數分別是c1、c2、c5、c10、c20、c50、c100。我們現在要用這些錢來支付K元,最少要用多少張紙幣呢?
在生活中,我們肯定是先用面值最大的來支付,如果不夠,就繼續用更小一點面值的,以此類推,最后剩下的用1元來補齊。

期望值:紙幣數目最少
限制值:每種面額的張數

在滿足限制值的情況下,希望用最少的紙幣數目達成期望值。不論使用面額大的紙幣還是面額小的紙幣,期望值的紙幣數目都會++,貢獻一樣。所以相同貢獻值的情況下,使用更大面額的紙幣更容易滿足期望值。

代碼如下:

int cmp(pair<int, int> a, pair<int, int> b) {return a.first > b.first;
}// Get the total num of papers that satisfy the K
// param1: nums of every coin
// param2: target number
// param3: cost of every coin in coins
void getCoinNums(vector<pair<int,int>> coins,int K, vector<pair<int,int>> &result) {if (coins.size() == 0) {return;}int i, tmp;// sort from biger to smallersort(coins.begin(), coins.end(), cmp);i = 0;tmp = 0;while(K && i < coins.size()) {tmp = K / coins[i].first;if (tmp != 0) {int real_nums;// defend the real coin nums overheadif (tmp <= coins[i].second) {real_nums = tmp;} else {real_nums = coins[i].second;}K -= real_nums * coins[i].first;result.push_back(make_pair(coins[i].first, real_nums));}i++;}
}

完整測試代碼:
https://github.com/BaronStack/DATA_STRUCTURE/blob/master/greedy/coins_charge.cc

2.3 最多覆蓋區間

假設我們有n個區間,區間的起始端點和結束端點分別是[l1, r1],[l2, r2],[l3, r3],…,[ln, rn]。我們從這n個區間中選出一部分區間,這部分區間滿足兩兩不相 交(端點相交的情況不算相交),最多能選出多少個區間呢?

比如區間: [6,8], [2,4], [3,5], [1,5], [5,9], [8,10], 則最多不相交區間為 : [2,4], [6,8], [8,10]

我們將各個區間的右端點從小到大排序,每次選擇區間時只需要確認當前區間的左端點比上一個區間的右端點大就可以了。之所以選擇對右端點進行從小到大的排序,因為右端點決定的是一個區間的下界,我們每次選擇盡可能選擇下界小且和之前的區間沒有相交的區間,則才能夠得到最多的不相交區間。

實現代碼如下:

int cmp(pair<int, int> a, pair<int, int> b) {if (a.second < b.second) {return 1;} else if (a.second == b.second &&a.first < b.first) {return 1;} else {return 0;}
}// algorithm:
// 1. Sort the array with right node increase
// 2. Maintain a num e, if rest of the interval's left node
// bigger than e, then the interval will be choosen
//
// example:
// Befor sort: [6,8], [2,4], [3,5], [1,5], [5,9], [8,10]
// After sort: [2,4], [1,5], [3,5], [6,8], [5,9], [8,10]
//
// result : [2,4], [6,8], [8,10]
void intervalCoverage(vector<pair<int,int>> intervals,vector<pair<int,int>> &result) {if (intervals.size() == 0) {return;}int i, e, count;sort(intervals.begin(), intervals.end(), cmp);e = -1;count = 0;for (i = 0;i < intervals.size(); i++) {if(intervals[i].first >= e) {count ++;e = intervals[i].second;result.push_back(make_pair(intervals[i].first,intervals[i].second));}}
}

完整測試代碼:
https://github.com/BaronStack/DATA_STRUCTURE/blob/master/greedy/interval_coverage.cc

2.4 哈夫曼編解碼

哈夫曼編碼是一種針對數據高效壓縮的編碼方式,能夠達到20%-90%的壓縮比。
比如:

  • 1000長度的字符串 需要1000bytes = 8000 bit的存儲空間。
  • 優化:如果1000長度的字符串中總共有8個不同的字符,則這八個字符可以用三個bit位就能表示(000-111),1000長度的字符串只需要3000 bit的存儲空間
  • 進一步優化:haffman編碼,每個字符的bit位表示可以不定長(解碼會復雜一些),總長度不會超過3位(1000字符不會超過3000bit的存儲空間)。haffman編碼為了滿足不定長的要求,不會出現針對某一個字符的編碼是另一個字符編碼前綴的情況。

比如如下字符表,huffman編碼 以及 其對應的總二進制位數 ,最后1000字符總共也只有2100的bit存儲空間。

所以這里根據頻率構建huffman 編碼的過程就用到了貪心的思想。

構建huffman樹的過程 選擇兩個小頻率的字符開始構建的父節點作為一個新節點,再選擇一個次小的與該新節點一起構建一個新的父節點,依次由下向上構建。最終出現頻率越高的字符串越靠近根節點。

構建過程如下:

void huffmanTree(priority_queue<Node> &q) {// root node is store in proiority_queue// when the q size is 1while (q.size() != 1) {Node *left = new Node(q.top()); q.pop();Node *right = new Node(q.top()); q.pop();// father's node and it's left and right childNode node('R', left->frequency + right->frequency, left, right);q.push(node);}
}

構建完huffman樹之后,進行各個字符的編碼,這里僅僅將所有的左子樹權值設置為0,又子樹權值設置為1就可以了

huffman編碼過程如下:

// Huffman encode function
// param1: Root is the huffman tree's root node
// param2: prefix is the encode result per char
// param3: a map with 'char' and it's huffman's encode
void huffmanEncode(Node *root, string &prefix,map<char, string> &result) {string m_prefix = prefix;if (root->left == nullptr)return;// set the left weight recursionprefix += "0";if (root->left->left == nullptr) {// find the char's result in the leaf noderesult[root->left->c] = prefix;} else {huffmanEncode(root->left, prefix, result);}// back to the begin node to set the right weightprefix = m_prefix;prefix += "1";if (root->right->right == nullptr) {result[root->right->c] = prefix;} else {huffmanEncode(root->right, prefix, result);}
}

huffman解碼如下:

// Huffman decode function
// param1: des is the input string to be decode
// param2: res is the map between char and huffman's
// encode string
// param3: decode string
bool huffmanDecode(string des, map <char, string> res,string &result) {if (des == "") {return false;}int i;map<char,string>::const_iterator it;string buf_str = "";for (i = 0; i < des.size(); i ++) {buf_str += des[i];for (it = res.begin() ; it != res.end(); it++ ) {if (it->second == buf_str) {result += it->first; buf_str = "";break;}}if(i == des.size() - 1 && it == res.end()) {return false;}}return true;
}

完整測試代碼:
https://github.com/BaronStack/DATA_STRUCTURE/blob/master/greedy/haffman.cc

總結

以上是生活随笔為你收集整理的贪心算法简单实践 -- 分糖果、钱币找零、最多区间覆盖、哈夫曼编解码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。