1. 程式人生 > 實用技巧 >《趣學演算法》第二章 貪心演算法原始碼

《趣學演算法》第二章 貪心演算法原始碼

目錄

貪心演算法相關程式碼實現

以下程式碼搬運自《趣學演算法》實戰演練

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;
}