貪心算法(會場安排問題、區間選點)

學習算法課程以後的第一次記錄,漸漸的,程序設計考慮的因素增多,程序=數據結構+算法,這個等式讓我深有體會。從開始簡單的C++編程,再到選擇合適數據結構,如今須要更進一步,從算法層次上考慮程序執行的效率。我對算法的理解是用更少的開銷得到更優的執行效果。node

分治法、動態規劃在此以前沒有記錄下來,學到貪心算法的時候,以爲須要總結一下學過的東西,也能更好的理解。動態規劃的設計,要知足最優子結構性質和重疊子問題,採用自底向上的策略,計算出最優值,找到總體最優解。這個過程有時候挺難的,主要在寫出遞歸式,要自底向上填表。貪心策略有點像動態規劃,但在一些方面是不一樣的,有時候貪心算法的思想更容易想到。它要知足子問題最優而獲得總體最優?兩個條件:最優子結構性質和貪心選擇性質。知足貪心選擇性質必定知足最優子結構性質,而知足最優子結構性質不必定知足貪心選擇性質,好比揹包問題能夠用貪心算法解決,而0-1揹包問題只能用動態規劃。ios

典型的貪心問題活動安排,有n個活動,給出開始時間和結束時間,要儘量安排多的活動(時間互相不衝突)。解決這個問題正確的貪心思想是以每一個活動結束時間爲比較變量,按結束時間升序排好活動次序,接着就進行比較選擇。而會場安排問題與活動又有些不一樣之處,下面是個人解題過程。c++

7-2 會場安排問題 (20 分)算法

假設要在足夠多的會場裏安排一批活動,並但願使用盡量少的會場。設計一個有效的 貪心算法進行安排。(這個問題其實是著名的圖着色問題。若將每個活動做爲圖的一個 頂點,不相容活動間用邊相連。使相鄰頂點着有不一樣顏色的最小着色數,相應於要找的最小 會場數。)編程

輸入格式:

第一行有 1 個正整數k,表示有 k個待安排的活動。 接下來的 k行中,每行有 2個正整數,分別表示 k個待安排的活動開始時間和結束時間。時間 以 0 點開始的分鐘計。數據結構

輸出格式:

輸出最少會場數。ide

輸入樣例:

5
1 23
12 28
25 35
27 80
36 50

輸出樣例:

3
#include<iostream>
#include<algorithm>
using namespace std;
struct node {
    int begin;
    int end;
    int flag;//標記該活動是否被安排,0表示未安排,1表示已安排 
}t[10001];
int cmp(const node &a,const node &b)//比較規則:以結束時間升序排列 
{ 
    return a.end<b.end;
 } 
int main()
{
    int i,j,n;
    node temp;
    cin>>n;
    for(i=0;i<n;i++) 
    {
        cin>>t[i].begin>>t[i].end;
        t[i].flag=0;
    }
    sort(t,t+n,cmp);
        
    int sum=0;//總共須要的會場數量 

    for(i=0;i<n;i++)//方法2 
    {
        if(!t[i].flag)//找到未安排的活動,進行場地安排 
        {
            sum++;
            int p=i;
            for(j=p+1;j<n;j++)//當前活動結束時間與下一個活動開始不相交 ,則安排到同一個會場 
            {
                if(t[p].end<=t[j].begin&&!t[j].flag)
                {
                    p=j;t[j].flag=1;
                }
            }
            t[i].flag=1;
        }
    }

    cout<<sum;
    return 0;
}
View Code

貪心策略爲:把儘量多的時間互不衝突的活動安排到一個會場,若活動時間交叉,則在安排到另外一個會場。函數

將全部活動按結束時間升序排列,利用sort函數,自定義cmp方法。循環體中,每次能夠找到尚未安排的活動,並以這個活動搜索能同時容納到一個會場的其餘活動(這一步嵌套在內層循環中),通過兩層循環,把全部活動所有安排好,這時也已經計算出須要的會場數量sum。學習

相似的問題是區間選點flex

7-10 選點問題 (15 分)
 數軸上有n個閉區間[ai, bi]。取儘可能少的點,使得每一個區間內都至少有一個點(不一樣區間內含的點能夠是同一個)。

輸入格式:

第一行一個數字n,表示有n個閉區間。 下面n行,每行包含2個數字,表示閉區間[ai, bi]

輸出格式:

一個整數,表示至少須要幾個點

輸入樣例:

在這裏給出一組輸入。例如:

3
1 3
2 4
5 6

輸出樣例:

在這裏給出相應的輸出。例如:2

開始想找出幾個區間共同段,而且記錄每一個共同段中包含哪些區間,這樣算出最少選點。後來發現以爲這個想法其實能夠簡化一下,策略爲:以右端爲擋板,看看前面是否包含其餘區間,若是是,則不記數,反之,說明沒有共同段,須要計數而且移動擋板位置繼續尋找。貪心策略是選擇區間右端點,保證可以包含更大交叉段,選的點最少。

#include<bits/stdc++.h>
using namespace std;
struct dot{
    int l,r;
    bool v[10001];
}dots[10001];

int cmp(const dot &a,const dot &b)//比較規則,按區間右端點升序排列 
{
    return a.r<b.r;
} 

int main()
{
    int n,i,j,count=1,select;
    cin>>n;
    for(i=0;i<n;i++)
        cin>>dots[i].l>>dots[i].r;
    sort(dots,dots+n,cmp);//預處理,將區間按規則排好序,方便後續比較 
    select=dots[0].r;
    //貪心策略是選擇區間右端點,保證可以包含更大交叉段,選的點最少 
    for(i=1;i<n;i++)//每次將當前選擇的一個區間的右端點與下一個(或者同一區間,可忽略)左端比較 
    {
        if(dots[i].l>select)//若是沒有交叉,選點+1,並以此區間右端爲新一輪比較的點 
        {
            count++;
            select=dots[i].r;
        }
    }
    cout<<count;
    return 0;
}
View Code

學習算法以後,發現解決問題上須要思惟上的改變,程序設計以前的算法選擇很重要,還要向大佬們學習,典型算法的學習研究真是博大精深呀!

相關文章
相關標籤/搜索