貪心算法(greedy algorithm)
貪心算法,顧名思義。其算法描述爲:在分步決策問題中,每一步選擇當前最優。由於貪心算法僅考慮局部最優性,所以不能保證整體最優。因此,對於貪心算法,必須進一步證明該算法的每一步做出的選擇都必然導致問題的一個整體最優解。
一般來說適用於貪心算法的問題具有兩個特性:最優度量標準和最優子結構。
貪心算法每一步做出的選擇可以依賴於以前做出的選擇,但決不依賴將來的選擇,也不依賴於子問題的解,分步做出最優選擇的過程是一個自頂向下的過程,這是該算法相對其它算法的獨特之處。
由於該算法比較簡單,我們僅用一個一般的揹包問題說明它。
一般揹包問題
問題描述:n個物品,第i個價值爲vi,重量爲wi,物品可分,揹包可承受的重量W,如何選入裝入揹包的物品,使裝入揹包的物品總價值最大。
#include <iostream>
#include <algorithm>
/*
問題表示:
共5間物品:
w:3 4 7 8 9
v:4 5 10 11 13
W:17
*/
const int W=17;
const int w[5]={3,4,7,8,9};
const int v[5]={4,5,10,11,13};
int ave_v_index[5]={0,1,2,3,4};//記錄平均重量價值的由大到小物品索引
bool cmp(int a,int b)
{
return ((double)v[a])/((double)w[a])>((double)v[b])/((double)w[b]);
}
double greedy_01()
{
std::sort(ave_v_index,ave_v_index+5,cmp);
int tem_w=0;
double sum_value=0;
int i=0;
for(;i<5;++i)
{
if(w[i]+tem_w<=W)
{
tem_w+=w[i];
sum_value+=v[i];
}else break;
}
if(i<5) sum_value+=((double)(W-tem_w))/((double)w[i])*((double)v[i]);
return sum_value;
}
int main(int argc, char** argv) {
std::cout<<greedy_01()<<std::endl;
return 0;
}
動態規劃(dynamic programming)
與貪心算法類似,動態規劃是一種求解最優問題的算法設計策略。動態規劃每一步的決策依賴於子問題的解,決策的過程自底向上,並保持子問題的解,從而可以避免重複子問題的計算。
此算法的主要特點是:通過採用表格技術,用多項式算法代替指數算法複雜度。
該算法的經典實例包括:優化的斐波拉契數列數、0-1揹包問題、最長公共子序列問題
斐波拉契數列
斐波拉契數列數(0,1,2,3,5,8,11,...)問題就不再描述了。
#include <iostream>
#include <algorithm>
#include <iterator>
int fbnq[1000];//用於保存計算的數
int get_fbnq(int num)
{
if(num==0) fbnq[0]=0;
else if(num==1) fbnq[1]=1;
else
fbnq[num]=get_fbnq(num-1)+get_fbnq(num-2);
return fbnq[num];
}
void print_seq(int *data,int num)
{
std::copy(data,data+num+1,std::ostream_iterator<int>(std::cout," "));
}
int main(int argc, char** argv) {
int num=10;
get_fbnq(num);
print_seq(fbnq,num);
return 0;
}
0-1揹包問題
問題描述:n個物品,第i個價值爲vi,重量爲wi,物品不可分,揹包可承受的重量W,如何選入裝入揹包的物品,使裝入揹包的物品總價值最大。
#include <iostream>
/*
問題表示:
共5間物品:
w:3 4 7 8 9
v:4 5 10 11 13
W:17
*/
const int W=17;
const int w[5]={3,4,7,8,9};
const int v[5]={4,5,10,11,13};
int sum_v[6][W+1];
int dynamic_01()
{
for(int i=0;i<=W;++i)
sum_v[0][i]=0;
for(int i=1;i<=5;++i)
sum_v[i][0]=0;
for(int i=1,k=0;i<=5;++k,++i)//自底向上 ,注意k是物品的編號,與i(放幾個物品)相差1
{
for(int j=1;j<=W;++j)
{
if((w[k]<=j)&&((v[k]+sum_v[i-1][j-w[k]]>sum_v[i-1][j])))
sum_v[i][j]=sum_v[i-1][j-w[k]]+v[k];
else sum_v[i][j]=sum_v[i-1][j];
std::cout<<"sum_value["<<i<<","<<j<<"]="<<sum_v[i][j]<<std::endl;
}
}
return sum_v[5][W];
}
int main(int argc, char** argv) {
std::cout<<dynamic_01()<<std::endl;
return 0;
}
最長公共子序列 LCS
問題描述:對兩個給定序列X={x1,x2,x3...xm}和Y={y1,y2,y3...yn},求它們的最長公共子序列。
#include <iostream>
char X[8]={'0','a','b','c','b','d','a','b'},
Y[7]={'0','b','d','c','a','b','a'};
int lcs[9][8];
int LCS()
{
for(int i=0;i<9;i++) lcs[i][0]=0;
for(int j=0;j<8;j++) lcs[0][j]=0;
for(int i=1;i<=8;++i)//自底向上
for(int j=1;j<=7;++j)
{
if(X[i]==Y[j]) //若xm=yn,那麼xm=yn=zk,且Zk-1是Xm-1和Xn-1的一條最長公共子序列。
lcs[i][j]=lcs[i-1][j-1]+1;
else//若 xm!=yn,那麼Z是Xm-1和Y或X和Yn-1的一條最長公共子序列。
lcs[i][j]=std::max(lcs[i-1][j],lcs[i][j-1]);
}
}
int main(int argc, char** argv) {
LCS();
std::cout<<lcs[8][7]<<std::endl;
return 0;
}