常見算法問題解決

  • 二分查找
#include<cstdio>
#include<iostream>
#include<bits/stdc++.h>
#define N 1001
using  namespace std;
int Num = 0;
double ans;
int binarySearch( const int b[], int searchKey, int low, int high ) {
        int middle;

        while( low <= high ) {
              middle = ( low + high ) / 2;
              if (searchKey == b[middle]) {
                  Num++;
                  return middle;
              }
              else if ( searchKey < b[middle] ) {
                  high = middle - 1;
                  Num++;
              }
              else {
                  low = middle + 1;
                  Num++;
              }
        }
        return -1; // not found
    }

int main()
{
    int arr[N];
    cout << "元素的個數" << setw(18) << "理論平均查找次數" << setw(22) << "實際平均查找次數" << endl;
    for ( int j=100; j <=1000; j+=100 )
    {
        Num = 0;
        arr[0]=10+rand()%15;
        for(int i=1; i<j; i++) {
              // 生成遞增隨機數列
              arr[i]=arr[i-1]+1+rand()%3+rand()%10;
        }
        int SearchTimes = 10;
        for (int time=0; time < SearchTimes; time++)
        {
            int index = rand() % j;	//  suffix 爲要搜索的元素下標
            int key = arr[index];	//    key 爲要查找的數據
            int result = binarySearch(arr, key, 0, j-1 );
        }
        ans = (double)(Num) / SearchTimes;
        cout << setw(6) << j << setw(18) << int( log(j)/log(2) )+0.5<< setw(22) <<ans << endl;

    }
    return 0;
}

  • 0-1揹包問題的動態規劃
#include<bits/stdc++.h>
#define N 101
using namespace std;
int n,w[N],v[N],W;//n個物品,重量存在w數組,價值存在v數組,揹包容量爲W
int c[N][N] = {0},x[N];//c[i][j]表示前i個物品放在容量爲j的揹包獲得的最大價值,x[i]表示第i個物品是否放入揹包
void compute()
{
    int i,j;
    for(i = 1;i <= n;i++)
        for(j = 1;j<= W;j++)
        {
            if(j < w[i])//物品重量大於揹包容量則不放
                c[i][j] = c[i-1][j];
            else //能放,則判斷放還是不放能使價值最大化
                c[i][j] = max(c[i-1][j],c[i-1][j-w[i]]+v[i]);
        }
    j = W;//回退判斷哪些物品放進去了
    for(i = n;i > 0;i--)
    {
        if(c[i][j] >c[i-1][j])
        {
            x[i] = 1;
            j -= w[i];
        }
        else
            x[i] = 0;
    }
    cout << "放入揹包的有:"<<endl;
    for(i = 1;i <= n;i++)
        if(x[i])
            cout <<i<<" ";
    cout << endl;
}
int main()
{
    cout <<"請輸入物品數和揹包容量:"<<endl;
    cin >> n >> W;
    cout <<"請依次輸入每個物品的重量和價值:"<<endl;
    for(int i = 1;i <= n;i++)
        cin >> w[i] >> v[i];
    compute();
    return 0;
}
/*測試數據
5 10
2 6 5 3 4 5 2 4 3 6
*/


  • 棋盤覆蓋的分治解法
#include <iostream>
#define N 1024
using namespace std;

int BOARD_SZ;
static int tile = 1;
static int board[N][N] = {0};
void chess_board(int tr, int tc, int dr, int dc, int size)
{
    if(size == 1)
        return;

    int t = tile++;
    int sz = size / 2;    //每次進行劃分

    if(dr < tr+sz && dc < tc+sz)  //注意一共四種情況,<>=這幾個符號要控制好邊界
        chess_board(tr, tc, dr, dc, sz);
    else{
        board[tr+sz-1][tc+sz-1] = t;
        chess_board(tr, tc, tr+sz-1, tc+sz-1, sz);
    }


    if(dr < tr+sz && dc >= tc+sz)   //notice < >=
        chess_board(tr, tc+sz, dr, dc, sz);
    else{
        board[tr+sz-1][tc+sz] = t;
        chess_board(tr, tc+sz, tr+sz-1, tc+sz, sz);
    }


    if(dr >= tr+sz && dc < tc+sz)   //notice >= <
        chess_board(tr+sz, tc, dr, dc, sz);
    else{
        board[tr+sz][tc+sz-1] = t;
        chess_board(tr+sz, tc, tr+sz, tc+sz-1, sz);
    }


    if(dr >= tr+sz && dc >= tc+sz)  //notice >= >=
        chess_board(tr+sz, tc+sz, dr, dc, sz);
    else{
        board[tr+sz][tc+sz] = t;                       //標記一個假設的特殊點
        chess_board(tr+sz, tc+sz, tr+sz, tc+sz, sz);   //遞歸該部分
    }
}

void print_chess_board()
{
    cout.setf(ios::left);     //左對齊
    for(int i=0; i<BOARD_SZ; ++i){
        for(int j=0; j<BOARD_SZ; ++j){
            cout.width(3);    //打印寬度爲3
            cout<<board[i][j];
        }
        cout<<endl;
    }
}

int main()
{
    int dr,dc;
    cout << "請輸入特殊方格的行列號和規格:"<<endl;
    cin >> dr >> dc >> BOARD_SZ;
    chess_board(0, 0, dr, dc, BOARD_SZ);
    print_chess_board();
    return 0;
}


  • 獨立任務的最優調度問題
//F[k,x]表示處理完第k個作業且A機器到達了時間x,B機器到達的最小時間,
//處理完第i個作業,如果是A處理的那麼F[i,x+a[i]] = b[i-1]
//否則F[i,x] =f[i-1,x]+ b[i]
#include<bits/stdc++.h>
#define N 1001
using namespace std;
int n,a[N] = {0},b[N] = {0},F[N][N] = {0},sumA = 0;//n個作業
void init()
{
    cout << "請輸入作業的個數: ";
    cin >> n;
    cout <<"請輸入作業在機器A上的時間:"<<endl;
    for(int i = 1;i <= n;i++)
    {
         cin >> a[i];
         sumA += a[i];
    }
    cout <<"請輸入作業在機器B上的時間:"<<endl;
    for(int i = 1;i <= n;i++)
        cin >> b[i];
}
void solve()
{
    for(int i = 1;i <= n;i++)
    {
        for(int j = 0;j <= sumA;j++)
        {
            if(j >= a[i])
                F[i][j] = min(F[i-1][j-a[i]],F[i-1][j]+b[i]);
            else
                F[i][j] = F[i-1][j] + b[i];
        }
    }
}
void print()
{
    int temp = 0,minv = 99999;
    for(int j = 0;j <= sumA;j++)//
    {
        temp = max(F[n][j],j);
        if(minv > temp)
            minv = temp;
    }
    cout<<"最小完成時間爲:"<<endl;
    cout << minv;
}
int main()
{
    init();
    solve();
    print();
    return 0;
}
/*
6
2 5 7 10 5 2
3 8 4 11 3 4
*/

  • 哈夫曼編碼
#include <iostream>
#include <queue>
using namespace std;
int bit[100] = {0};//用於存儲遞歸調用時的編碼
class Node{
public:
    int id;//在數組中的索引
    int parent;
    int lchild;
    int rchild;
    float weight;//權重,即字符的出現頻率
    char value;
    //缺省構造函數,確定成員的初始值
    Node():id(-1),parent(-1),lchild(-1),rchild(-1),weight(0),value(' '){}
    void setValue(int _id,int _lchild, int _rchild, float _weight)
    {
        id = _id;
        //parent = _parent;
        lchild = _lchild;
        rchild = _rchild;
        weight = _weight;
    }
    //重寫 < 運算符,使其能夠在優先隊列中正確的排序
    bool operator<(const Node &a)const
    {
        return a.weight < weight;
    }
};
int n,k;
Node* haff;//一個指向Node型數組的指針
priority_queue<Node> pq;//存儲節點的優先隊列
vector<Node> v;
void init()
{
    char c;
    float f;
    cout<<"請輸入字符的個數:"<<endl;
    cin >> n;
    k = n;
    haff = new Node[n+n+2];
    cout<<"請依次輸入字符值和頻率"<<endl;
    for(int i = 1;i<= n;i++)
    {
        cin >>c>>f;
        haff[i].value = c;
        haff[i].weight = f;
        haff[i].id = i;
        pq.push(haff[i]);
    }
}
void bulidHaff()
{
    while(pq.size() > 1)
    {
        Node a,b,c;
        a = pq.top();
        pq.pop();
        b = pq.top();//取出權值最小的兩棵子樹合併成新的子樹
        pq.pop();
        k++;
        //計算新和成的子樹的權重等,並放到數組中,方便確定編碼方式
        haff[k].setValue(k,a.id,b.id,a.weight+b.weight);//爲什麼直接++k不行?
        pq.push(haff[k]);//新樹插入到優先隊列中
    }
}
void dfs(Node h,int num)
{
    if(h.value >= 97 && h.value <= 122)
    {
        cout<<"字符 "<<h.value<<" 的編碼爲:";
        for(int i = 0;i < num;i++)
            cout<<bit[i];
        cout<<endl;
        return ;
    }
    else//右子樹編碼爲1,左子樹編碼爲0
    {
        if(h.rchild != -1)
        {
            dfs(haff[h.rchild],num+1);
            bit[num] = 1;
        }
        if(h.lchild != -1)
        {
            dfs(haff[h.lchild],num+1);
            bit[num] = 0;
        }
    }
}
int main()
{
    init();
    bulidHaff();
    dfs(pq.top(),0);//遞歸調用求出字符的編碼
    return 0;
}
/*測試數據
6
a 0.05
b 0.32
c 0.18
d 0.07
e 0.25
f 0.13
*/

  • 加油站問題的貪心實現
#include<bits/stdc++.h>
#define N 1001
using namespace std;
//dis數組存儲加油站之間的距離,sum[i]表示存儲第一個到第i個加油站間的總距離
int dis[N] = {0},n,k,sum[N] = {0};
bool flag = true;//表示是否能達到終點
int station = 0;//停靠的次數
void init()
{
    cout<<"請輸入汽車滿油最大行駛距離和加油站的數量:"<<endl;
    cin >> n >> k;
    cout<<"請依次輸入加油站之間短距離:"<<endl;
    for(int i = 1;i <=k+1;i++)
    {
        cin >> dis[i];
        sum[i] = sum[i-1]+dis[i];
        if(sum[i] - sum[i-1] > n)//如果有兩個相鄰加油站之間距離大於n,則不可能到達終點
            flag = false;
    }
}
void greedy()
{
    if(!flag)
    {
        cout<<"Sorry,No Solution!"<<endl;
        return ;
    }
    //start代表上一次的停靠點,stop代表本次將要停靠的點
    int start = 0,stop;
    cout<<"停靠的加油站有:"<<endl;
    for(stop = 1;stop < k+2;stop++)
    {
        //貪心選擇一個剛好汽車行駛最大距離能達到的加油站
        if(sum[stop] - sum[start] <= n && sum[stop+1] - sum[start] > n)
        {
             station++;
             start = stop;
             cout <<stop<<" ";
        }
        //最後一站是終點,停車不加油,正好sum[k+2]等於0,station不會加
    }
    cout << k+1<<endl;
}
int main()
{
    init();
    greedy();
    cout<<"最少加油的次數是: "<<station<<endl;
    for(int i = 1;i <= k+1;i++)
        cout<<sum[i]<<" ";
    return 0;
}
/*測試數據
7 7
1 2 3 4 5 1 6 6
*/

  • 圖的m着色問題
//圖的m着色問題
#include<bits/stdc++.h>
#define N 101
using namespace std;
int n,color,bian,m[N];//節點數,顏色數,和邊的條數,m[i]表示第i個節點的顏色
int pict[N][N] = {0},ans = 0;//圖的鄰接矩陣
void init()
{
    cin >> n >> color >> bian;
    for(int i = 1;i <= bian;i++)
    {
        int a,b;
        cin >>a >> b;
        pict[a][b] = 1;
        pict[b][a] = 1;
    }
}
bool ok(int k)
{
    for(int j = 1;j < k;j++)
    {
        if(pict[k][j] && m[k] == m[j])
            return false;
    }
    return true;
}
void backtrack(int k)
{
    if(k > n)
    {
        cout << "第" << ++ans << "種方案:" << endl;
        for(int i = 1;i <= n;i++)
            cout << m[i] << " ";
        cout << endl;
        return ;
    }
    for(int c = 1; c <= color; c++)
    {
        m[k] = c;
        if(ok(k))
            backtrack(k+1);
        //回退時m[k]不影響前面的取值,所以不需要改回去
    }
}
int main()
{
    init();
    backtrack(1);
    return 0;
}
/*測試數據
7 3 12
1 2
1 3
1 4
2 3
2 5
3 4
3 5
4 5
4 7
5 6
5 7
6 7
*/

  • 0-1揹包的回溯實現
//0-1揹包問題的回溯算法實現
#include<bits/stdc++.h>
#define N 1001
using namespace std;
int n,w[N] = {0},v[N] = {0};//n見物品,第i件物品重量爲w[i],價值爲v[i]
int cw = 0,cv = 0,we,bestv = 0;//cw表示已經放入揹包的重量,cv表示已經放入揹包的價值,we表示揹包的容量,bestw表示最優值即最大價值
int x[N],best[N];//x表示回溯過程中的解,best記錄最優的解,1爲放入,0不放
void init()
{
    cout << "請輸入物品數量和揹包容量,用空格分開:" <<endl;
    cin >> n >> we;
    cout << "請依次輸入 " << n << " 件物品的重量和價值,用空格分開:"<<endl;
    for(int i = 1;i <= n;i++)
    {
        cin >> w[i] >> v[i];
    }
}
int Bound(int k)//上界函數,返回已放入的物品價值和剩下的價值和
{
    int sum = 0;
    for(int i = k;i <= n;i++)
        sum += v[i];
    return (cv + sum);
}
void backTrack(int k)//代表第k層
{
    if(k > n)//一直搞不懂爲什麼到了最後一層就一定是最優解,不用判斷以前的bestv和本次bestv哪個大嗎,仔細一想,能到達最後一層的情況要麼n件物品全部放入,(不滿足限界條件就剪紙了,到不了最後)
    {        //肯定是最大值;要麼也是滿足限界函數Bound(k+1) > bestv才能到達最後一層,所以肯定比上次的bestv大
        int sum = 0;
        for(int j = 1;j <= n;j++)
        {
            best[j] = x[j];
            sum += (x[j] * v[j]);
        }
        bestv = sum;
        return ;
    }
    if(cw + w[k] < we)//滿足約束條件,即還能放入第k個物品,搜索左子樹
    {
        x[k] = 1;
        cw += w[k];
        cv += v[k];
        cout << "cv: " << cv << endl;
        backTrack(k+1);
        cw -= v[k];
        cv -= v[k];
    }
    if(Bound(k+1) > bestv)//第k個揹包不放入,如果滿足上界函數也有可能得到最優解,擴展右子樹
    {
        x[k] = 0;
        backTrack(k+1);
    }
}
void print()
{
    cout << "最優裝包價值爲:"<<bestv<<endl;
    cout << "裝進揹包的物品編號爲:" << endl;
    for(int  i = 1;i <= n;i++)
        if(best[i])
        cout << i << " ";
}
int  main()
{
    init();
    backTrack(1);//從第一個揹包開始判斷
    print();
    return 0;
}
/*測試數據
5 10
2 6 5 3 4 5 2 4 3 6
*/

  • 旅行售貨商問題
#include<bits/stdc++.h>
#define INF 9999
#define N 1001
using namespace std;
int n, m;
int g[N][N], x[N], best[N];//城市的鄰接矩陣,路線的臨時保存,最優路線
int c = 0, bestl = INF;//目前經過城市的路徑和,最優路徑和
void init()
{
    cout << "請輸入城市數和邊數:";
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    {
        x[i] = i;
        for(int j = 1;j <= n;j++)
            g[i][j] = INF;
    }
    cout << "請依次輸入兩個相連城市的編號和距離:" << endl;
    for(int i = 1;i <= m;i++)
    {
        int x1,x2,x3;
        cin >> x1 >> x2 >> x3;
        g[x1][x2] = g[x2][x1] = x3;
    }
}
void travel(int k)
{
    if(k > n)
    {
        if(g[x[n]][1] != INF && c+g[x[n]][1] < bestl)//最後一個節點和第一個節點有通路且,加上最後一段距離小於目前最優長度
        {
            bestl = c+g[x[n]][1];
            for(int i = 1;i <= n;i++)
                best[i] = x[i];
            return ;
        }
    }
    else
    {
        for(int j = k;j <= n;j++)
        {
            //有通路且可能產生最優解
            if(g[x[k-1]][x[j]] != INF && c + g[x[k-1]][x[j]] < bestl)
            {
                swap(x[k],x[j]);
                c += g[x[k-1]][x[k]];
                travel(k+1);
                c -= g[x[k-1]][x[k]];
                swap(x[k],x[j]);
            }
        }
    }
}
void print()
{
    cout <<"最短路徑爲: " << bestl << endl << "路線是:" <<endl;;
    for(int i = 1;i <= n;i++)
        cout << best[i] << "->";
    cout << "1"<< endl;
}
int main()
{
    init();
    travel(2);
    print();
    return 0;
}
/*測試數據
5 9
1 2 3
1 4 8
1 5 9
2 3 3
2 4 10
2 5 5
3 4 4
3 5 3
4 5 20
*/

趣學算法

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