貪心算法介紹與使用

貪心算法思想:

   貪心算法不是從整體來考慮, 而是在某種意義上在局部做出最優選擇. 對於有些問題局部最優不能代表整體最優選擇,是不能使用貪心算法,例如01揹包問題.

貪心算法是自頂向下的方式分解各子問題, 用迭代的方式相繼做出貪心選擇, 每一次的選擇的子問題再簡化成規模更小的問題來選擇。

由於貪心算法與動態規劃很類似, 在解決問題時需要確定問題是否具有貪心選擇的性質.


貪心算法思路:

  1.   從最初問題的初始解開始

  2.   while  (將問題分解) do

          求出分解的問題的一個解

  3.   然後由所有解組合成問題的一個可行的解。


爲什麼對與01揹包問題不能使用貪心算法來解決問題?

假設現在有一個揹包重30,有A,B,C三種物品且重量與價值分別如下:

物品: A   B   C

重量:29 13 13

價值:32 19 19

根據價值最大的貪心策略,那麼答案爲A,但是B+C纔是最優解。

對於根據單位重量價格最大的貪心策略的反例:

物品:A B C

重量:29 22 10

價值:29 22 10

所以貪心算法並不適合01揹包問題, 應該使用動態規劃來解決這個問題。

對於01揹包問題不能用貪心算法解是因爲:貪心算法將問題分解成子問題時是用的單一的條件,如果此條件與其他因素關聯的話,那麼貪心策略就會

失敗。從01揹包問題來看,按照價值最大來劃分子問題時受到物品重量條件約束,所以貪心算法解決的問題是講問題劃分成子問題的條件是獨立的。



貪心算法例子1

設n爲一自然數,n可以分解成若干個不同的自然數的和,這樣的分法有很多種,比如n=10, 10可以分解爲:10=5+4+1; 10=5+3+2; 10+9+1; 10=8+2; 10=7+3; 10=6+4; 10=7+2+1; 10=6+3+1;…。在所有這些分法中,各加數乘積最大的爲30, (10=5+3+2中加數的乘積爲5*3*2=30)。試編寫程序,求各種分解方法中各加數乘積的最大值。

輸入要求:輸入只有1行,自然數n。

輸出要求:輸出也只有1行,所有分解方法中各加數乘積的最大值


這是一個具有貪心性質的問題, 將10分解成若干數,找到符合條件的組合數,然後再從組合數中挑出乘積最大的值。符合貪心算法的思路。將問題分爲若干解, 再由所有局部最優解得到整體的解。

參考代碼:

#include<iostream>
#include<vector>
using namespace std;
class MaxProductNumber {
public:
    MaxProductNumber() :
            maxNum(0) {
    }
    ~MaxProductNumber() {
    }
    int getTmpMaxNum();
    int getMaxProductNumber() {
        return maxNum;
    }
    bool isExistAtVec(int val);
    void Solution(int n, int curValue, int num);
public:
    int maxNum;           //保存最大乘積
    vector<int> vec;     //保存每次獲取的n的拆分數
};
bool MaxProductNumber::isExistAtVec(int val) {
    if (vec.size() <= 0) {
        return false;
    }
    for (int i = 0; i < vec.size(); ++i) {
        if (val == vec.at(i)) {
            return true;
        }
    }
    return false;
}
int MaxProductNumber::getTmpMaxNum() {
    int tmpMax = 1;
    for (int i = 0; i < vec.size(); i++) {
        tmpMax *= vec.at(i);
        cout << vec.at(i) << " ";
    }
    cout << endl;
    return tmpMax;
}
void MaxProductNumber::Solution(int n, int curValue, int num) {
    if (num == n) {
        int tmp = getTmpMaxNum();
        if (tmp > maxNum) {
            maxNum = tmp;
        }
        return;
    }
    for (int i = curValue; i < n; i++) {
        if (!isExistAtVec(i)) {
            num += i;
            vec.push_back(i);
            Solution(n, curValue + 1, num);
            vec.pop_back();
            num -= i;
        }
    }
}
int main() {
    int n = 0;
    cin >> n;
    MaxProductNumber maxProduct;
    maxProduct.Solution(n, 1, 0);
    cout << maxProduct.getMaxProductNumber() << endl;
    return 0;
}

貪心算法例子2

有n個人參加一個馬拉松接力遊戲,遊戲規定每個人可以根據自己的情況隨時終止遊戲並由下一個人繼續接力。由於每個人的情況不同,即使同一個人也不可能在整 個遊戲過程中永遠保持很好的狀態。因此要求他們在比賽前根據每個人的情況需要制定一個接力規則,使整個比賽的時間越少越好。請編寫程序幫助他們制定這樣的 接力方案。 
輸入要求:輸入的第1行有三個整數n,k和m,分別表示參加接力的人的個數,每個人最多可以跑的公里數以及接力賽的距離(以公里爲單位)。其後的n行,每 行有k個整數,分別表示每個人跑整數1公里,2公里,….,K公里所花費的時間(以秒爲單位,整數)。遊戲要求每個人都必須參加比賽,且每次只能跑到整數 公里後才能換人。 
輸出要求:輸出1個整數,表示這些人跑完整個接力賽最少要花多少時間

這個問題也是具有貪心性質,和例子1是一樣的,只不過解的規則複雜了一點但是解題思路是一樣的。

解題思路:

(1)將m拆成若干自然數的和,m =1+1+1+..., m=1+2+2.., m=1+2+3+...

(2)找到m拆成的若干自然數的個數p滿足 p == n, 並且 若干自然數都<=k, 即 p[i] <=k  (0<i<n)

(3)然後根據獲得的p,從每個人k公里跑的時間得到這組p的最短時間t

(4)彙總獲得的t, 找到最少的t,即爲最終結果。

#include<iostream>
#include<vector>
using namespace std;
class Result {
public:
    Result() :
        minTime(-1) {
    }
    ~Result() {
    }
    void setMinTime(int n, int k);
    int getMinTime() {
        return minTime;
    }
    void Solution(int m, int n, int k, int num);
    void setVecTime(int n, int k);
    void showVecTime(int n, int k);
    int getAllVecValue();
public:
    long minTime;           //保存最少消耗時間
    vector<int> vec;     //保存符合條件的拆分數
    vector<vector<long> > vecTime;  //保存每個人k公里跑的時間
};
void Result::showVecTime(int n, int k) {
    for (int i = 0; i < n; i++) {
        vector<long> vv = vecTime.at(i);
        for (int j = 0; j < k; j++) {
            cout<<vv.at(j)<<" ";
        }
        cout<<endl;
    }
}
void Result::setVecTime(int n, int k) {
    for (int i = 0; i < n; i++) {
        vector<long> vecc;
        int val;
        for (int j = 0; j < k; j++) {
            cin >> val;
            vecc.push_back(val);
        }
        vecTime.push_back(vecc);
    }
}
void Result::setMinTime(int n, int k) {
    //先判斷所有的拆分數都小於k
    for(int i = 0; i < vec.size(); i++) {
        if (vec.at(i) > k) {
            return;
        }
    }
    int tmpTime = 0;
    vector<int> indexVec(n);
    for (int i = 0; i < vec.size(); i++) {
        long tmp = 1000000; 
        int index = 0;
        for (int j = 0; j < n; j++) {
            if (indexVec.at(j) <= 0 && vecTime[j].at(vec.at(i) - 1) < tmp) {
                tmp = vecTime[j].at(vec.at(i) - 1);
                index = j;
            }
        }
        indexVec.at(index) = 1;
        index = 0;
        tmpTime += tmp;
    }
    if (minTime == -1 || minTime > tmpTime) {
        minTime = tmpTime;
    }
}
int Result::getAllVecValue() {
    int tmp = 0;
    for (int i = 0; i < vec.size(); i++) {
        tmp += vec.at(i);
    }
    return tmp;
}
void Result::Solution(int m, int n, int k, int num) {
    if (num >= m) {
        if (vec.size() == n && getAllVecValue() == m) {
            setMinTime(n, k);
        }
        return;
    }
    for (int i = 1; i <= m; i++) {
            num += i;
            vec.push_back(i);
            Solution(m, n, k, num);
            vec.pop_back();
            num -= i;
    }
}

int main() {
    int m, n, k;
    vector<vector<long> > vecTime;
    Result result;
    cin>>m>>n>>k;
    result.setVecTime(n, k);
    result.Solution(m, n, k, 0);
    cout <<result.getMinTime()<< endl;
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章