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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基本算法之贪心算法

發布時間:2025/3/19 编程问答 10 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基本算法之贪心算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

貪心算法

  • 防嗮
    • 難點:
    • 技巧:
  • 畜欄預定
    • 難點
    • 技巧
  • 雷達設備
  • 給樹染色
    • 如何合并節點
    • 為什么可以這樣簡化、

防嗮

題目鏈接

解題思路:

我們首先將奶牛可以承受的最小值,遞減排序,也就是降序排列,然后將防曬霜固定的值,遞減排序,還是降序排列.
對于每一個頭奶牛而言,當然是要選擇目前來說滿足條件的最差的防曬霜,什么最差的定義,就是選擇滿足奶牛條件的SPF最大的那一瓶防曬霜.
注意:降序排序,保證對于每一頭牛而言,它用的是,可以使用的最差的防曬霜,因為值越小的防曬霜,有可能讓更多的牛使用.而升序排列,就恰好反了. 注意:如果一頭牛沒有合適的防嗮霜給它了就不用管他了。

難點:

  • 如何高效的找到小于等于一個值的最大值
  • 如何動態刪除一個元素。(用完了要刪除)
  • 區間排序

技巧:

  • 以上兩個操作是平衡樹的兩個基本操作因此我們可以使用平衡樹來解決。在STL中有map 和 set
  • 對于區間來說我們可以使用pair ,因為pair 內部安排了默認的排序方式:以first 為關鍵字 升序排列。
  • 設置哨兵是為了讓查找不為空不返回空。

拓展知識點:

增廣路徑: 預先選一些匹配的邊,從任一個不在匹配的點,先沿著非匹配邊,再沿著匹配邊走,。。。,交替,最后找到一個不在匹配里的點。(就找到了一個增廣路徑)。 如果一個匹配沒有增廣路徑,則達到了最大匹配。

代碼:

#include<stdio.h> #include<map> #include<algorithm> using namespace std;typedef pair<int ,int > pll; const int N = 2510;int n ,m; pll cows[N];int main(){scanf("%d%d",&n,&m);for(int i=0;i<n;++i)scanf("%d%d",&cows[i].first,&cows[i].second);sort(cows,cows+n);map<int,int >spfs;for(int i=0;i<m;++i){int x,cover;scanf("%d%d",&x,&cover);spfs[x] += cover;}int ans = 0;spfs[0] = spfs[1001] = n; // 哨兵,避免使用 查找函數時出錯for(int i = n-1;i >= 0;--i){auto cow = cows[i];// 大于的最小的值auto it = spfs.upper_bound(cow.second); // 以區間的右邊找 ,難點一it--; // 找到小于等于的最大值if(it->first >= cow.first && it->first <= cow.second){ans ++;if(-- it->second == 0)spfs.erase(it); // 難點二}}printf("%d\n",ans);return 0; }

畜欄預定

題目鏈接

解題思路:

  • 將所有牛按開始吃草的時間排序;
  • 用小根堆維護當前所有畜欄的最后一頭牛的吃草結束時間;
  • 如果當前的牛可以安排在堆頂的畜欄中,則將其安排進去,否則就新建一個畜欄;

難點

  • 對于每頭牛我們既要記錄區間,還要記錄編號。
  • 小根堆(即圍欄)的更新操作。
  • 記錄每頭牛進入的圍欄的編號。

技巧

  • 對于第一個難點我們可以用 pair<pair<int,int>,int>的方式來存儲。
  • 對于第二個難點,我們小根堆里的元素類型也是 pair 這樣我們記錄兩個元素一個了一頭牛吃完草結束的時間,另一個是圍欄的編號。所以在更新操作的時候我們把頂部的圍欄拿出來修改得時候先把其刪除,在更新結束時間的時候,把原來圍欄的編號記錄下。
  • 因為我們每頭牛都有一個編號,而圍欄也有,所以我們用一個數組來存儲這兩個對應的關系。其中牛的編號就是 0~n-1 ,圍欄編號就是創建時小根堆中圍欄元素個數。

代碼:

#include<stdio.h> #include<vector> #include<algorithm> #include<queue>using namespace std;typedef pair<int ,int> PII ;pair <PII,int> cows[50010]; int n; int id[50010];int main (){scanf("%d",&n);for(int i = 0;i<n;++i){// 難點一就是如何存這么多元素scanf("%d%d",&cows[i].first.first,&cows[i].first.second);cows[i].second = i;}sort(cows,cows+n);priority_queue<PII,vector<PII>,greater<PII>>heap;for(int i=0;i<n;++i){auto cow = cows[i].first;// 新建一個圍欄if(heap.empty() || heap.top().first >= cow.first){PII stall = {cow.second ,heap.size() + 1 };id[cows[i].second] = stall.second;heap.push(stall);}else{// 難點二,更新圍欄PII stall = heap.top();heap.pop();stall.first = cow.second;id[cows[i].second] = stall.second;heap.push(stall);}}printf("%d\n",heap.size());for(int i = 0;i <n ;++i)printf("%d\n",id[i]);return 0; }

雷達設備

題目鏈接

解題思路
如下圖所示,對于任意一個小島 (x,y)(x,y),我們都可以在海岸線上求出能覆蓋該小島的建造雷達的區間 [a,b]。

由勾股定理,將所有小島轉化成區間后,問題轉化為:給定 n 個區間,在 x軸上選擇盡量少的點,使得所有區間至少包含一個點。

算法步驟:

  • 將所有區間按右端點從小到大排序;
  • 依次考慮每個區間:
    如果當前區間包含最后一個選擇的點,則直接跳過;
    如果當前區間不包含最后一個選擇的點,則在當前區間的右端點的位置選 一個新的點;
  • 代碼:

    #include<stdio.h> #include<algorithm> #include<math.h> using namespace std;typedef pair<double,double> PDD; const double eps = 1e-6 , INF = 1e10; int n,R; PDD segs[1010]; bool success ; int main(){success = 1;scanf("%d%d",&n,&R);for(int i = 0;i< n;++i){int x,y;scanf("%d%d",&x,&y);if(y > R){success = 0;break;}double len = sqrt(R * R - y * y );segs[i] = {x + len,x - len};}if(!success) puts("-1");else{sort(segs,segs + n);int ans = 0;double pos = - INF;for(int i = 0;i<n;++i){auto seg = segs[i];if(seg.second - pos > eps){ans ++ ;pos = seg.first;}}printf("%d\n",ans);}return 0; }

    給樹染色

    題目鏈接

    解題思路
    每次找出當前權值最大的非根節點,將其染色順序排在緊隨父節點之后的位置,然后將該點合并進父節點中,更新父節點的權值。直到將所有點都合并進根節點為止。

    如果直接按上述算法做的話,最終的分值不太容易計算,我們可以在將點合并的時候,實時更新當前的權值和:

    • 最初所有點各自為一組,總分值是 S=∑i=1nai?1S=\sum_{i=1}^n a_i?1S=i=1n?ai??1
    • 接下來每次會將兩組點合并,將其中一組點接在另一組點的后面。比如兩組點分別是 xix_ixi?yiy_iyi?,我們將 yiy_iyi?接在 xix_ixi? 之后,則 yiy_iyi?中每個點所乘的系數均會增加一個相同的偏移量,這個偏移量就是 xix_ixi?中點的個數,假設是 k,則合并之后,總的權值直接加上 k?∑yik?∑y_ik?yi?即可;

    難點:

    • 如何合并節點
    • 為什么可以這樣簡化

    如何合并節點



    因此可以看到只要將父節點的關系轉化以下就好。

    為什么可以這樣簡化、

    我們看把 y 的一堆節點放到x 后邊 ,x 的值是不會變得,而 y 每一個節點的值相當于 乘上 x 中 的節點個數。

    代碼:

    #include<stdio.h> #include<algorithm>using namespace std;const int N = 1010; struct node{int father,size,sum;double avg; }nodes[N];int n ,root; // 根據 平均值,找到最大的點 int find(){int pos = -1 ;double maxx = 0;for(int i= 1;i <= n ;++i){if( i != root && maxx < nodes[i].avg){maxx = nodes[i].avg;pos = i;}}return pos; }int main(){int ans = 0;scanf("%d%d",&n,&root);for(int i = 1;i <=n; ++i){node &nd = nodes[i];scanf("%d",&nd.sum);nd.size = 1 ;nd.avg = nd.sum;ans += nd.sum; // }// 建立關系for(int i = 0, a, b;i< n-1;++i ){scanf("%d%d",&a,&b);nodes[b].father = a;}// 遍歷 n - 1 次for(int i = 0;i< n -1; ++ i){int ver = find();int fa = nodes[ver].father;// 當前節點的 總和 乘上 父節點 的大小ans += nodes[ver].sum * nodes[fa].size;// 用完這后將該節點刪除,//因為 找節點是用find () 函數 依據avg 值找,所以置為 -1 就不會用到了nodes[ver].avg = -1;// 難點 :合并節點,遍歷所有節點。// 將原本是其 子節點 的直接連上其父節點即可for(int j = 1 ; j <= n ;++j)if(nodes[j].father == ver)nodes[j].father = fa;// 難點三 : 更新父節點的信息nodes[fa].size += nodes[ver].size ;nodes[fa].sum += nodes[ver].sum;nodes[fa].avg = (double)nodes[fa].sum / nodes[fa].size;}printf("%d\n",ans);return 0; }

    寫在最后,感謝y總的講解。

    總結

    以上是生活随笔為你收集整理的基本算法之贪心算法的全部內容,希望文章能夠幫你解決所遇到的問題。

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