《趣學演算法》第二章 貪心演算法原始碼
阿新 • • 發佈:2020-09-02
目錄
貪心演算法相關程式碼實現
以下程式碼搬運自《趣學演算法》實戰演練
1、加勒比海盜船——最優裝載問題
#include <iostream> #include <algorithm> const int N=1000005; using namespace std; double w[N]; //古董的重量陣列 int main() { double c; int n; cout<<"請輸入載重量c及古董個數n:"<<endl; cin>>c>>n; cout<<"請輸入每個古董的重量,用空格分開: "<<endl; for(int i=0;i<n;i++) { cin>>w[i]; //輸入每個物品重量 } sort(w,w+n); //按古董重量升序排序 double tmp=0.0; int ans=0; // tmp為已裝載到船上的古董重量,ans為已裝載的古董個數 for(int i=0;i<n;i++) { tmp+=w[i]; if(tmp<=c) ans++; else break; } cout<<"能裝入的古董最大數量為Ans="; cout<<ans<<endl; return 0; } //可以輸出古董編號 /** struct antique{ int id; //古董的編號 double w; //古董的重量 }s[N]; bool cmp(antique a, antique b)//比較函式 { return a.w < b.w; //指明按照古董重量升序排列 } int main() { double c; int n; cout<<"請輸入載重量c及古董個數n:"<<endl; cin>>c>>n; cout<<"請輸入每個古董的重量,用空格分開: "<<endl; for(int i=0;i<n;i++) { s[i].id=i+1; cin>>s[i].w; //輸入每個古董重量,用空格隔開 } sort(s,s+n,cmp); double tmp=0.0; int ans =0; //ans記錄已經裝載的古董個數,tmp代表裝載到船上的古董的重量 for(int i=0;i<n;i++) { tmp += s[i].w; if(tmp<=c) ans ++; else break; } cout<<"能裝入的古董最大數量為Ans = "; cout<<ans<<endl; cout<<"裝入的古董編號為"; for(int i=0;i<ans;i++) { cout<<s[i].id<<" "; } return 0; } **/
2、阿里巴巴與四十大盜——揹包問題
//program 2-2 #include<iostream> #include<algorithm> using namespace std; const int M=1000005; struct three{ double w;//每個寶物的重量 double v;//每個寶物的價值 double p;//價效比 }s[M]; bool cmp(three a,three b) { return a.p>b.p;//根據寶物的單位價值從大到小排序 } int main() { int n;//n 表示有n個寶物 double m ;//m 表示毛驢的承載能力 cout<<"請輸入寶物數量n及毛驢的承載能力m :"<<endl; cin>>n>>m; cout<<"請輸入每個寶物的重量和價值,用空格分開: "<<endl; for(int i=0;i<n;i++) { cin>>s[i].w>>s[i].v; s[i].p=s[i].v/s[i].w;//每個寶物單位價值 } sort(s,s+n,cmp); double sum=0.0;// sum 表示貪心記錄運走寶物的價值之和 for(int i=0;i<n;i++)//按照排好的順序貪心 { if( m>s[i].w )//如果寶物的重量小於毛驢剩下的承載能力 { m-=s[i].w; sum+=s[i].v; } else//如果寶物的重量大於毛驢剩下的承載能力 { sum+=m*s[i].p;//部分裝入 break; } } cout<<"裝入寶物的最大價值Maximum value="<<sum<<endl;//輸出裝入寶物的最大價值 return 0; }
3、高階鐘點祕書——會議安排
//program 2-3 #include <iostream> #include <algorithm> #include <cstring> using namespace std; struct Meet { int beg; //會議的開始時間 int end; //會議的結束時間 int num; //記錄會議的編號 }meet[1000]; //會議的最大個數為1000 class setMeet{ public: void init(); void solve(); private: int n,ans; // n:會議總數 ans: 最大的安排會議總數 }; //讀入資料 void setMeet::init() { int s,e; cout <<"輸入會議總數:"<<endl; cin >> n; int i; cout <<"輸入會議的開始時間和結束時間,以空格分開:"<<endl; for(i=0;i<n;++i) { cin>>s>>e; meet[i].beg=s; meet[i].end=e; meet[i].num=i+1; } } bool cmp(Meet x,Meet y) { if (x.end == y.end) return x.beg > y.beg; return x.end < y.end; } void setMeet::solve() { sort(meet,meet+n,cmp); //對會議按結束時間排序 cout <<"排完序的會議時間如下:"<<endl; int i; cout <<"會議編號:"<<" 開始時間 "<<" 結束時間"<<endl; for(i=0; i<n;i++) { cout<< " " << meet[i].num<<"\t\t"<<meet[i].beg <<"\t"<< meet[i].end << endl; } cout <<"-------------------------------------------------"<<endl; cout << "選擇的會議的過程:" <<endl; cout <<" 選擇第"<< meet[0].num<<"個會議" << endl;//選中了第一個會議 ans=1; int last = meet[0].end; //記錄剛剛被選中會議的結束時間 for( i = 1;i < n;++i) { if(meet[i].beg>=last) { //如果會議i開始時間大於等於最後一個選中的會議的結束時間 ans++; last = meet[i].end; cout <<" 選擇第"<<meet[i].num<<"個會議"<<endl; } } cout <<"最多可以安排" <<ans << "個會議"<<endl; } int main() { setMeet sm; sm.init();//讀入資料 sm.solve();//貪心演算法求解 return 0; }
4、一場說走就走的旅行——最短路徑
//棧實現
#include <iostream>
#include<windows.h>
#include<stack>
using namespace std;
const int N=100; // 城市的個數可修改
const int INF=1e7; // 無窮大10000000
int map[N][N],dist[N],p[N],n,m;//n城市的個數,m為城市間路線的條數
bool flag[N]; //如果s[i]等於true,說明頂點i已經加入到集合S;否則頂點i屬於集合V-S
void Dijkstra(int u)
{
for(int i=1; i<=n; i++)
{
dist[i] =map[u][i]; //初始化源點u到其他各個頂點的最短路徑長度
flag[i]=false;
if(dist[i]==INF)
p[i]=-1; //源點u到該頂點的路徑長度為無窮大,說明頂點i與源點u不相鄰
else
p[i]=u; //說明頂點i與源點u相鄰,設定頂點i的前驅p[i]=u
}
dist[u] = 0;
flag[u]=true; //初始時,集合S中只有一個元素:源點u
for(int i=1; i<=n; i++)
{
int temp = INF,t = u;
for(int j=1; j<=n; j++) //在集合V-S中尋找距離源點u最近的頂點t
if(!flag[j]&&dist[j]<temp)
{
t=j;
temp=dist[j];
}
if(t==u) return ; //找不到t,跳出迴圈
flag[t]= true; //否則,將t加入集合
for(int j=1;j<=n;j++)//更新與t相鄰接的頂點到源點u的距離
if(!flag[j]&& map[t][j]<INF)
if(dist[j]>(dist[t]+map[t][j]))
{
dist[j]=dist[t]+map[t][j] ;
p[j]=t ;
}
}
}
void findpath(int u)
{
int x;
stack<int>s;
cout<<"源點為:"<<u<<endl;
for(int i=1;i<=n;i++)
{
x=p[i];
while(x!=-1)
{
s.push(x);
x=p[x];
}
cout<<"源點到其它各頂點最短路徑為:";
while(!s.empty())
{
cout<<s.top()<<"--";
s.pop();
}
cout<<i<<";最短距離為:"<<dist[i]<<endl;
}
}
int main()
{
int u,v,w,st;
system("color 0d");
cout << "請輸入城市的個數:"<<endl;cin >> n;
cout << "請輸入城市之間的路線的個數:"<<endl;cin >>m;
cout << "請輸入城市之間的路線以及距離:"<<endl;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
map[i][j]=INF;//初始化鄰接矩陣為無窮大
}
while(m--)
{
cin >> u >> v >> w;
map[u][v] =min(map[u][v],w); //鄰接矩陣儲存,保留最小的距離
}
cout <<"請輸入小明所在的位置:"<<endl; ;
cin >> st;
Dijkstra(st);
cout <<"小明所在的位置:"<<st<<endl;
for(int i=1;i<=n;i++)
{
cout <<"小明:"<<st<<" - "<<"要去的位置:"<<i;
if(dist[i] == INF)
cout << "sorry,無路可達"<<endl;
else
cout << " 最短距離為:"<<dist[i]<<endl;
}
findpath(st);
return 0;
}
//佇列實現
#include <queue>
#include <iostream>
#include<cstring>
#include<windows.h>
using namespace std;
const int N = 100; // 城市的個數可修改
const int INF = 1e7; // 無窮大
int map[N][N],dist[N],n,m;
int flag[N];
struct Node{
int u,step;
Node(){};
Node(int a,int sp){
u=a;step=sp;
}
bool operator < (const Node& a)const{ // 過載 <
return step>a.step;
}
};
void Dijkstra(int st){
priority_queue <Node> Q; // 優先佇列優化
Q.push(Node(st,0));
memset(flag,0,sizeof(flag));//初始化flag陣列為0
for(int i=1;i<=n;++i)
dist[i]=INF; // 初始化所有距離為,無窮大
dist[st]=0;
while(!Q.empty())
{
Node it=Q.top();//優先佇列隊頭元素為最小值
Q.pop();
int t=it.u;
if(flag[t])//說明已經找到了最短距離,該結點是佇列裡面的重複元素
continue;
flag[t]=1;
for(int i=1;i<=n;i++)
{
if(!flag[i]&&map[t][i]<INF){ // 判斷與當前點有關係的點,並且自己不能到自己
if(dist[i]>dist[t]+map[t][i])
{ // 求距離當前點的每個點的最短距離,進行鬆弛操作
dist[i]=dist[t]+map[t][i];
Q.push(Node(i,dist[i]));// 把更新後的最短距離壓入優先佇列,注意:裡面的元素有重複
}
}
}
}
}
int main()
{
int u,v,w,st;
system("color 0d");//設定背景及字型顏色
cout << "請輸入城市的個數:"<<endl;
cin >> n;
cout << "請輸入城市之間的路線的個數:"<<endl;
cin >>m;
for(int i=1;i<=n;i++)//初始化圖的鄰接矩陣
for(int j=1;j<=n;j++)
{
map[i][j]=INF;//初始化鄰接矩陣為無窮大
}
cout << "請輸入城市之間u,v的路線以及距離w:"<<endl;
while(m--)
{
cin>>u>>v>>w;
map[u][v]=min(map[u][v],w); //鄰接矩陣儲存,保留最小的距離
}
cout<<"請輸入小明所在的位置:"<<endl; ;
cin>>st;
Dijkstra(st);
cout <<"小明所在的位置:"<<st<<endl;
for(int i=1;i<=n;i++)
{
cout <<"小明:"<<st<<"--->"<<"要去的位置:"<<i;
if(dist[i]==INF)
cout << "sorry,無路可達"<<endl;
else
cout << " 最短距離為:"<<dist[i]<<endl;
}
return 0;
}
5、神祕電報密碼——哈夫曼編碼
//program 2-5
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXBIT 100
#define MAXVALUE 10000
#define MAXLEAF 30
#define MAXNODE MAXLEAF*2 -1
typedef struct
{
double weight;
int parent;
int lchild;
int rchild;
char value;
} HNodeType; /* 結點結構體 */
typedef struct
{
int bit[MAXBIT];
int start;
} HCodeType; /* 編碼結構體 */
HNodeType HuffNode[MAXNODE]; /* 定義一個結點結構體陣列 */
HCodeType HuffCode[MAXLEAF];/* 定義一個編碼結構體陣列*/
/* 構造哈夫曼樹 */
void HuffmanTree (HNodeType HuffNode[MAXNODE], int n){
/* i、j: 迴圈變數,m1、m2:構造哈夫曼樹不同過程中兩個最小權值結點的權值,
x1、x2:構造哈夫曼樹不同過程中兩個最小權值結點在陣列中的序號。*/
int i, j, x1, x2;
double m1,m2;
/* 初始化存放哈夫曼樹陣列 HuffNode[] 中的結點 */
for (i=0; i<2*n-1;i++)
{
HuffNode[i].weight=0;//權值
HuffNode[i].parent=-1;
HuffNode[i].lchild=-1;
HuffNode[i].rchild=-1;
}
/* 輸入 n 個葉子結點的權值 */
for (i=0; i<n; i++)
{
cout<<"Please input value and weight of leaf node "<<i+1<<endl;
cin>>HuffNode[i].value>>HuffNode[i].weight;
}
/* 構造 Huffman 樹 */
for (i=0; i<n-1; i++)
{//執行n-1次合併
m1=m2=MAXVALUE;
/* m1、m2中存放兩個無父結點且結點權值最小的兩個結點 */
x1=x2=0;
/* 找出所有結點中權值最小、無父結點的兩個結點,併合並之為一棵二叉樹 */
for (j=0;j<n+i;j++)
{
if (HuffNode[j].weight<m1&&HuffNode[j].parent==-1)
{
m2 = m1;
x2 = x1;
m1 = HuffNode[j].weight;
x1 = j;
}
else if (HuffNode[j].weight < m2 && HuffNode[j].parent==-1)
{
m2=HuffNode[j].weight;
x2=j;
}
}
/* 設定找到的兩個子結點 x1、x2 的父結點資訊 */
HuffNode[x1].parent = n+i;
HuffNode[x2].parent = n+i;
HuffNode[n+i].weight = m1+m2;
HuffNode[n+i].lchild = x1;
HuffNode[n+i].rchild = x2;
cout<<"x1.weight and x2.weight in round "<<i+1<<"\t"<<HuffNode[x1].weight<<"\t"<<HuffNode[x2].weight<<endl; /* 用於測試 */
}
}
/* 哈夫曼樹編碼 */
void HuffmanCode(HCodeType HuffCode[MAXLEAF], int n){
HCodeType cd; /* 定義一個臨時變數來存放求解編碼時的資訊 */
int i,j,c,p;
for(i=0;i<n;i++)
{
cd.start=n-1;
c=i;
p=HuffNode[c].parent;
while(p!=-1)
{
if(HuffNode[p].lchild==c)
cd.bit[cd.start]=0;
else
cd.bit[cd.start]=1;
cd.start--; /*前移一位 */
c=p;
p=HuffNode[c].parent; /* 設定下一迴圈條件 */
}
/* 把葉子結點的編碼資訊從臨時編碼cd中複製出來,放入編碼結構體陣列 */
for (j=cd.start+1; j<n; j++)
HuffCode[i].bit[j]=cd.bit[j];
HuffCode[i].start=cd.start;
}
}
int main()
{
int i,j,n;
cout<<"Please input n:"<<endl;
cin>>n;
HuffmanTree(HuffNode,n); //構造哈夫曼樹
HuffmanCode(HuffCode,n); // 哈夫曼樹編碼
//輸出已儲存好的所有存在編碼的哈夫曼編碼
for(i=0;i<n;i++)
{
cout<<HuffNode[i].value<<": Huffman code is: ";
for(j=HuffCode[i].start+1;j<n;j++)
cout<<HuffCode[i].bit[j];
cout<<endl;
}
return 0;
}
6、溝通無限校園網——最小生成樹
//program 2-7
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100;
int nodeset[N];
int n, m;
struct Edge {
int u;
int v;
int w;
}e[N*N];
bool comp(Edge x, Edge y) {
return x.w < y.w;
}
void Init(int n)
{
for(int i = 1; i <= n; i++)
nodeset[i] = i;
}
int Merge(int a, int b)
{
int p = nodeset[a];
int q = nodeset[b];
if(p==q) return 0;
for(int i=1;i<=n;i++)//檢查所有結點,把集合號是q的改為p
{
if(nodeset[i]==q)
nodeset[i] = p;//a的集合號賦值給b集合號
}
return 1;
}
int Kruskal(int n)
{
int ans = 0;
for(int i=0;i<m;i++)
if(Merge(e[i].u, e[i].v))
{
ans += e[i].w;
n--;
if(n==1)
return ans;
}
return 0;
}
int main() {
cout <<"輸入結點數n和邊數m:"<<endl;
cin >> n >> m;
Init(n);
cout <<"輸入結點數u,v和邊值w:"<<endl;
for(int i=1;i<=m;i++)
cin >> e[i].u>> e[i].v >>e[i].w;
sort(e, e+m, comp);
int ans = Kruskal(n);
cout << "最小的花費是:" << ans << endl;
return 0;
}
//program 2-7-1
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100;
int father[N];
int n, m;
struct Edge {
int u;
int v;
int w;
}e[N*N];
bool comp(Edge x, Edge y) {
return x.w < y.w;
}
void Init(int n)
{
for(int i = 1; i <= n; i++)
father[i] = i;
}
int Find(int x)
{
if(x != father[x])
father[x] = Find(father[x]);
return father[x];
}
int Merge(int a, int b)
{
int p = Find(a);
int q = Find(b);
if(p==q) return 0;
if(p > q)
father[p] = q;//小的賦值給大的集合號
else
father[q] = p;
return 1;
}
int Kruskal(int n)
{
int ans = 0;
for(int i=0;i<m;i++)
if(Merge(e[i].u, e[i].v))
{
ans += e[i].w;
n--;
if(n==1)
return ans;
}
return 0;
}
int main() {
cout <<"輸入結點數n和邊數m:"<<endl;
cin >> n >> m;
Init(n);
cout <<"輸入結點數u,v和邊值w:"<<endl;
for(int i=1;i<=m;i++)
cin>>e[i].u>>e[i].v>>e[i].w;
sort(e, e+m, comp);
int ans = Kruskal(n);
cout << "最小的花費是:" << ans << endl;
return 0;
}