算法考试

Ex1-10 平均情况下的时间复杂度

T a v g ( N ) = ∑ I ∈ D N P ( I ) T ( N , I ) ≤ ∑ I ∈ D N P ( I ) max ⁡ I ′ ∈ D n T ( N , I ′ ) = T ( N , I ∗ ) ∑ I ∈ D N P ( I ) = T ( N , I ∗ ) = T m a x ( N ) T_{avg}(N)=\sum_{I\in D_N}P(I)T(N,I)\le \sum_{I\in D_N}P(I)\max_{I'\in D_n} T(N,I') \\=T(N,I^*)\sum_{I\in D_N}P(I) \\=T(N,I^*)=T_{max}(N) Tavg(N)=IDNP(I)T(N,I)IDNP(I)IDnmaxT(N,I)=T(N,I)IDNP(I)=T(N,I)=Tmax(N)
因此
T m a x ( N ) = Ω ( T a v g ( N ) ) = Ω ( θ ( f ( n ) ) = Ω ( f ( n ) ) T_{max}(N)=\Omega(T_{avg}(N))=\Omega(\theta(f(n))=\Omega(f(n)) Tmax(N)=Ω(Tavg(N))=Ω(θ(f(n))=Ω(f(n))

Ex2-30 石油管道

某石油公司计划建造一晶由东向西的主输油管道。诙管道暨穿过一个有n门油井的油田。从每口油井都要有一条输油管道沿最短路经(或南或北)与主管道相连。如果给定n口油井的位置,即它们的x坐标和y坐标,应如何确定主管道的最优位置,即使各油井到主管道之间的输油管道长度总和最小的位置?证明可在线性时间内确定主管道的最优位置。

解析

O ( n ) O(n) O(n)查找第k小的算法如下,所以只需要调用 R a n d o m i z e d S e l e c t ( y [ ] , 0 , n − 1 , ( n + 1 ) / 2 ) RandomizedSelect(y[], 0, n-1, (n+1)/2) RandomizedSelect(y[],0,n1,(n+1)/2)
该算法实际上是模仿快速排序算法设计出来的的。其基本思想也是对输入数组进行递归划分。与快速排序算法不间的是,它只对划分出的子数组之一进行递归处理。

template <class Type>
Type RandomizedSelect(Type a[], int p, int r, int k){
    if (p==r) return a[p];
    int i = RandomizedPartition(a, p, r);
    j = i-p+1;
    if (k <= j) return RandomizedSelect(a, p, i, k);
    else return RandomizedSelect(a, i+1, r, k-j);
}

int RandomizedPartition(Type a[], int p, int r){
    int i = p;
    int j = r+1;
    int rand = Random(p, r);
    Swap(a[p], a[rand]);
    Type x = a[p];
    while(true){
        while (a[++i] < x);
        while (a[--j] > x);
        if (i>=j) break;
        Swap(a[i], a[j]);
        
    }
    a[p] = a[j];
    a[j] = x;
    return j;
}

Ex2-33 马遍历

考虑国际象棋棋盘上某个位置的一只马,它是否可能只走63步,正好走过除起点外的其他63个位置各一次?如果有种这样的走法,则称所走的这条路线为一条马的周游路线。试设计一个分治算法找出这样的一条马的周游路线。

定义 1:如果能从棋盘上最后遍历的一个点回到第一个点,则称这种马的遍历是闭合的。

定义 2:如果马的遍历路径包含下图中的所有路径,则称这种马的遍历是结构性的。
在这里插入图片描述

在这里插入图片描述

bool Knight::comp(int m, int n, int offx, int offy){
    int m1, m2, n1, n2;
    int x[8], y[8], p[8];
    if (odd(m) || odd(n) || m-n>2 || n-m>2 || m< 6 || n < 6)return true;
    if (m < 12 || n<12){base(m, n, offx, offy); return 0;}
    m1 = m/2;
    if (m%4 > 0) --m1;
    m2 = m-m1;
    n1 = n/2;
    if (n%4 > 0) --n1;
    n2 = n-n1;
    
    //分割步
    comp(m1, n1, offx, offy);
    comp(m1, n2, offx, offy+n1);
    comp(m2, n1, offx+m1, offy);
    comp(m2, m2, offx+m1, offy+n1);
    //合并步
    x[0] = offx+m1-1;y[0] = offy+n1 - 3;
    x[1] = x[0]-1;y[1] = y[0]+2;
    x[2] = x[1]-1;y[2] = y[1]+2;
    x[3] = x[2]+2;y[3] = y[2]-1;
    x[4] = x[3]+1;y[4] = y[3]+2;
    x[5] = x[4]+1;y[5] = y[4]-2;
    x[6] = x[5]+1;y[6] = y[5]-2;
    x[7] = x[6]-2;y[7] = y[6]+1;
    
    for (int i = 0;i < 8;++i) p[i] = pos(x[i], y[i], n);
    for (int i = 1;i < 8; i += 2){
        int j1 = (i+1)%8, j2 = (i+2)%8;
        if (link[x[i]][y[i]].x == p[i-1])
            link[x[i]][y[i]].x = p[j1];
        else
            link[x[i]][y[i]].y = p[j1];
        
        if (link[x[j1]][y[j1]].x == p[j2])
            link[x[j1]][y[j1]].x = p[i];
        else
            link[x[j1]][y[j1]].y = p[i];
        
    }
    
}

void Knight::base(int m, int n, int offx, int offy){
    if (m==6&&n==6) build(m, n, offx, offy, N, b66);
    if (m==6&&n==8) build(m, n, offx, offy, N, b68);
    if (m==8&&n==6) build(m, n, offx, offy, N, b86);
    if (m==8&&n==10) build(m, n, offx, offy, N, b810);
    if (m==10&&n==8) build(m, n, offx, offy, N, b108);
    if (m==10&&n==10) build(m, n, offx, offy, N, b1010);
    if (m==10&&n==12) build(m, n, offx, offy, N, b1012);
    if (m==12&&n==10) build(m, n, offx, offy, N, b1210);
    
}

void Knight::build(int m, int n, int offx, int offy, int col, grid *b){
    int i, p, q, k = m*n;
    for (i = 0;i < k;++i){
        int x1 = offx+b[i].x, y1 = offx+b[i].y;
        int x2 = offx+b[(i+1)%k].x, y2 = offy+b[(i+1)%k].y;
        p = pos(x1, y1, col);
        q = pos(x2, y2, col);
        link[x1][y1].x = q;
        link[x2][y2].y = p;
    }
}

int Knight::pos(int x, int y, int col){
    return col*x + y;
}

在这里插入图片描述

Ex2-34 格雷码

分治法构造格雷码
在这里插入图片描述

void gray(int n){
    if (n==1){
        a[1] = 0;
        a[2] = 1;
        return;
    }
    gray(n-1);
    for (int k = 1<<(n-1),i=k;i>0;--i){
        a[2*k-i+1] = k + a[i];
    }
}

void out(int n){
    
    char str[32];
    int m = 1<<n;
    for (int i = 1;i <= m;++i){
        _itoa(a[i], str, 2);
        int len = strlen(str);
        for (int j = 0;j < n-len;++j) cout << '0';
        cout << str << " ";
    }
    cout << endl;
}

T ( n ) = { 1 n = 1 T ( n − 1 ) + 2 n − 1 n &gt; 1 T(n)=\begin{cases} 1 &amp; n=1 \\ T(n-1) + 2^{n-1} &amp; n &gt;1 \end{cases} T(n)={1T(n1)+2n1n=1n>1
解此递归方程的,算法的时间复杂度为 O ( 2 n ) O(2^n) O(2n)

Ex2-35 日程安排

设n个运动员要进行网球循环赛。设计一个满足以下要求的比赛日程表:
(1) 每个选手必须与其他n-1个选手各赛一次。
(2)每个选手一天只能赛一次。
(3)当n是偶数时,循环赛进行n-1天。当n是奇数时,循环赛进行n天

void tournament(int n){
    if (n==1){
        a[1][1] = 1;
        return;
    }
    if (odd(n)){
        tournament(n/2);
        makecopy(n);
    }
}
bool odd(int n){
    return n & 1;
}

void makecopy(int n){
    if (n/2>1 && odd(n/2))
        copyodd(n);
    else
        copy(n);
}

void copyodd(int n){
    int m = n/2;
    for (int i = 1;i <= m;++i)
    {
        b[i] = m+i;
        b[m+i] = b[i]
    }
    for (int i = 1;i <= m; ++i){
        for (int j = 1;j <=m+1;++j){
            if (a[i][j] > m){
                a[i][j] = b[i];
                a[i+m][j] = (b[i]+m)%n;
            }
            else{
                a[i+m][j] = a[i][j]+m;
            }
        }
        for (int j = 2;j <=m;++j){
            a[i][m+j] = b[i+j-1];
            a[b[i+j-1]][m+j] = i;
        }
    }
}

Ex3-3 作业调度

对于给定的2台处理机A和B处理n个作业,找出一个最优调度方案,使两台机器处理完这n个作业的时间最短。

分析:
(1)计算出 m = max ⁡ { max ⁡ 1 ≤ i ≤ n { a i } , max ⁡ 1 ≤ i ≤ n { b i } } m=\max\{\max_{1\le i\le n}\{a_i\},\max_{1\le i\le n}\{b_i\}\} m=max{1inmax{ai},1inmax{bi}}
(2)设布尔量p(i,j,k)表示前k个作业可以在处理机A用时不超过i,在处理机B用时不超过j时间内完成。动态转移方程如下 p ( i , j , k ) = p ( i − a k , j , k − 1 ) ∣ p ( i , j − b k , k − 1 ) p(i,j,k)=p(i-a_k,j,k-1) | p(i,j-b_k,k-1) p(i,j,k)=p(iak,j,k1)p(i,jbk,k1)
(3)最短时间为:
min ⁡ 0 ≤ i ≤ m n , 0 ≤ j ≤ m n , p ( i , j , n ) = t r u e { m a x ( i , j ) } \min_{0\le i \le mn,0\le j \le mn,p(i,j,n)=true}\{max(i,j)\} 0imn,0jmn,p(i,j,n)=truemin{max(i,j)}

code:

void dyna(){
    
    for (int i = 0;i <= mn;++i)
        for (int j = 0;j <=mn;++j){
            p[i][j][0] = true;
            for (int k = 1;k <=n;++k)p[i][j][k] = false;
        }
    for (int k = 1;k <= n;++k)
        for (int i = 0;i <= mn;++i)
            for (int j = 0;j <= mn;++j){
                if (i >= a[k-1])
                    p[i][j][k] = p[i-a[k-1]][j][k-1];
                if (j >= b[k-1]
                    p[i][j][k] = p[i][j-b[k-1]][k-1];
            }
    int opt = mn;
    for (int i = 0;i <= mn;++i)
        for (int j =0; j <= mn;++j)
            if (p[i][j][n]){
                opt = min(opt, max(i,j));
            }
}

时间复杂度: O ( m 2 n 3 ) O(m^2n^3) O(m2n3)

Ex3-7 漂亮打印

void BeautifulPrint(){
    c[0] = 0;
    l[0] = maxint;
    for (int i = 1;i <= n;++i){
        int j = i;
        extra = M-l[j];
        while (extra >= 0){
            if (i == n)
                lc = 0;
            else
                lc = extra * extra * extra;
            if (c[i]>c[j-1]+lc){
                c[i] = c[j-1] + lc;
                p[i] = j-1;
            }
            --j;
            extra = extra - l[j] - 1
        }
    }
    
}

最后一行从p[n]+1开始打印,倒数第二行从p[p[n]]+1开始打印。
时间复杂度: O ( M n ) O(Mn) O(Mn)
空间复杂度: O ( n ) O(n) O(n)

Ex3-9 石子合并

在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分,并分析算法的复杂性

int MinStoneMerge(vector<int> a){
    int n = a.size()-1;
    for (int i = 1;i < n;++i) a.push_back(a[i]);

    int s[2*n] = {0};
    for (int i = 1;i <= 2*n-1;++i)
        s[i] = s[i-1]+a[i];
    int m[2*n][2*n] = {0};

    for (int l = 2;l <= n;++l)
    for (int i = 1;i <= 2*n-l;++i){
        int j = i+l-1;
        m[i][j] = INT_MAX;
        for (int k = i;k < j;++k)
            m[i][j] = min(m[i][j], m[i][k]+m[k+1][j]+s[j]-s[i-1]);
    }
    int ret = INT_MAX;
    for (int i = 1;i <= n;++i)
        ret = min(ret, m[i][i+n-1]);
    return ret;

}


int MaxStoneMerge(vector<int> a){
    int n = a.size()-1;
    for (int i = 1;i < n;++i) a.push_back(a[i]);

    int s[2*n] = {0};
    for (int i = 1;i <= 2*n-1;++i)
        s[i] = s[i-1]+a[i];
    int m[2*n][2*n] = {0};

    for (int l = 2;l <= n;++l)
    for (int i = 1;i <= 2*n-l;++i){
        int j = i+l-1;
        m[i][j] = INT_MIN;
        for (int k = i;k < j;++k)
            m[i][j] = max(m[i][j], m[i][k]+m[k+1][j]+s[j]-s[i-1]);
    }
    int ret = INT_MIN;
    for (int i = 1;i <= n;++i)
        ret = max(ret, m[i][i+n-1]);
    return ret;

}

Ex3-21 最大子立方体

解析:此问题可以看做最大子段和在三维上的推广,详见课本pdf 67

int MaxSum(int n, int *a){
    int sum = 0, b = 0;
    for (int i = 1;i <= n;++i){
        if (b>0)
            b += a[i];
        else
            b = a[i];
        sum = max(sum, b);
    }
    return sum;
}

int MaxSum2(int m, int n, int **a){
    int sum = 0;
    int *b = new int[n+1];
    for (int i = 1;i <=m;++i){
        memset(b, 0, sizeof(b));
        for (int j = i;j <=m;++j){
            for (int k = 1;k <= n;++k) b[k] += a[j][k];
            sum = max(sum, MaxSum(n, b));
        }
    }
    return sum;
}

int MaxSum3(const vector<matrix<int> > &a){
    int sum = 0;
    int m = a.size(), n = a[0].rows(), p = a[0].cols();
    int b[n][p] = {0};
    for (int i = 0;i <m;++i){
        memset(b, 0, sizeof(b));
        for (int j = i;j < m;++j){
            for (int k = 0;k < n;++k)
            for (int t = 0;t < p;++t)
                b[k][t] += a[j][k][t];
            
            sum = max(sum, MaxSum2(n, p, b));
        }
    }
    return sum;
}

Ex4-2 活动安排

在这里插入图片描述

Ex4-26 数列极差

贪心策略:
max: a,b每次取数列最小的两个数
min:a,b每次取数列最大的两个数
code:

int max(int *a, int n)
{
    priority_queue<int> q;
    int i;
    for(i=0; i<n; i++)
        q.push(a[i]);
    int data1, data2, data;
    while(q.size()>=2)  //一直合并下去,直到队列中只剩下一个数
    {
        data1 = q.top(); q.pop();
        data2 = q.top(); q.pop();
        data = data1 * data2 + 1;
        q.push(data);
    }
    data = q.top();
    q.pop();
    return data;
}

int min(int *a, int n)
{
    priority_queue<int, vector<int>, greater<int> > q;
    int i;
    for(i=0; i<n; i++)
        q.push(a[i]);
    int data1, data2, data;
    while(q.size()>=2)  //一直合并下去,直到队列中只剩下一个数
    {
        data1 = q.top(); q.pop();
        data2 = q.top(); q.pop();
        data = data1 * data2 + 1;
        q.push(data);
    }
    data = q.top();
    q.pop();
    return data;
}
--------------------- 
作者:谛听- 
来源:CSDN 
原文:https://blog.csdn.net/u012319493/article/details/50001057 
版权声明:本文为博主原创文章,转载请附上博文链接!

Dijkstra Algorithm

在这里插入图片描述

迭代Sudist[2]dist[3]dist[4]dist[5]
初始1-10MAX30100
11,22106030100
21,2,4410503090
312,4,3310503060
41,2,4,3,5510503060

code:

template<class Type>
void Dijkstra(int n, int v, Type dist[], int prev[], Type **c)
{
    bool s[n+1] = {false};
    for (int i = 1;i <= n;++i){
        dist[i] = c[v][i];
        if (dist[i] == INT_MAX) prev[i] = 0;
        else prev[i] = v;
    }
    dist[v] = 0;s[v] = true;
    for (int i =1;i < n;++i){
        int temp = INT_MAX;
        int u = v;
        for (int j = 1;j <= n;++j){
            if ((!s[j]) && (dist[j]<temp)){
                u = j;
                temp = dist[j];
            }
        }
        s[u] = true;
        for (int j = 1;j <= n;++j)
            if ((!s[j])&&(c[u][j]<maxint)){
                Type newdist = dist[u] + c[u][j];
                if (newdist < dist[j]){
                    dist[j] = newdist;
                    prev[j] = u;
                }
            }        
    }
    
}

时间复杂度: O ( n 2 ) O(n^2) O(n2)

Ex5-4 01背包

template <class Typew, class Typep>
class Knap{
	friend Typep Knapsack(Typep*, Typew*, Typew, Typew, int, int[]);
	private:
		Typep Bound(int i);
		void Backtrack(int i);
		Typew c;//背包容量
		int n;//物品数
		int *x;//当前解
		int *bestx;//当前最优解
		Typew * w;//物品重量数组
		Typep * p;//物品价值数组
		Typew cw;//当前重量
		Typep cp;//当前价值
		Typep bestp;//当前最有价值

}

template <class Typew, class Typep>
Type Knap<Typew, Typep>::Bound(int i){
    Typew left = c-cw;
    TYpep b = cp;
    while (i <=n && w[i] <= left){
        left -= w[i];
        b += p[i];
        ++i;
    }
    if (i <= n) b += p[i]/w[[i] * left;
    return b;
}

class Object{
    friend int Knapsack(int *, int*, int, int);
public:
    int operator <= (Object a) const{
        return (d >= a.d);
    }
private:
    int id;
    float d;
};

template <class Typew, Typep>
void Knap<Typew, Typep>::Backtrack(int i){
    if (i>n){
        for (int j = 1;j <= n;++i) bestx[j] = x[j];
        bestp = cp;
        return;
    }
    if (cw + w[i] <= c){
        x[i] = 1;
        cw += w[i];
        cp += p[i];
        Backtrack(i+1);
        x[i] = 0;
        cw -= w[i];
        cp -= p[i];

    }
    if (Bound(i+1) > bestp)
        Backtrack(i+1);
}

template<class Typew, class Typep>
Typep Knapsack(Typep p[], Typew w[], Typew c, int n, int bestx[]){
    Typew W = 0;
    Typep P = 0;
    Object *Q = new Object[n];
    for (int i = 1;i <= n;++i){
        Q[i-1].id = i;
        Q[i-1].d = 1.0 * p[i]/w[i];
        P += p[i];
        W += w[i];
    }
    if (W <= c) return P;
    Sort(Q, n);
    Knap<Typew, Typep> k;
    k.p = new Typep[n+1];
    k.w = new Typew[n+1];
    k.x = new int[n+1];
    for (int i = 1;i<=n;++i){
        k.p[i] = p[Q[i-1].id];
        k.w[i] = w[Q[i-1].id];
        k.x[i] = 0;
    }
    k.cp = 0;
    k.cw = 0;
    k.c = c;
    k.n = n;
    k.bestp = 0;
    k.bestx = bestx;
    k.Backtrack(1);
    for (int i =1;i <=n;++i) k.x[i] = k.bestx[i];
    for (int i =1;i <= n;++i)k.bestx[Q[i-1].id] = k.x[i];
    delete []Q;
    delete []k.w;
    delete []k.p;
    delete []k.x;
    return k.bestp;
}

在这里插入图片描述

Ex5-5 最大团问题

在这里插入图片描述
code

class Clique{

void Backtrack(int i);
int **a,//图G的邻接矩阵
    n,//图G的顶点数
    *x,// 当前解
    *bestx,//当前最优解
    cn,//当前顶点数
    bestn;//当前最大顶点数
}

void Clique::iterClique(){
    for (int i = 0;i <= n;++i) x[i] = 0;
    i = 1;
    while (true){
        while (i <= n && ok(i)){
            x[i++] = 1;
            ++cn;
        }
        if (i >= n){
            for (int j = 1;j <=n;++j) bestx[j] =x[j];
            bestn = cn;
        }
        else
            x[i++] = 0;
        while (cn+n-i <= bestn){
            --i;
            while (i && !x[i]) --i;
            if (i==0) return ;
            x[++i] = 0;
            --cn;
        }
    }
}

bool Clique::ok(int i){
    for (int j = 1;j < i;++j)
        if (x[j] && a[i][j]==0)
            return false;
    return true;
}

时间复杂度: O ( 2 n ) O(2^n) O(2n)

Ex-5-14 运动员最佳匹配

class pref{
private:
    int best;
    int n;
    int *r;
    int **p, **q;
}

void pref::Backtrack(int t){
    if (t > n){
        Compute();
    }

    else{
        for (int j = t;j <= n;++j){
            swap(r[t], r[j]);
            Backtrack(t+1);
            swap(r[t], r[j]);
        }
    }
}

void pref::Compute(void){
    int temp = 0;
    for (int i = 1; i <= n;++i){
        temp += p[i][r[i]]*q[r[i]][i];
    }
    if (temp > best){
        best = temp;
        for (int i = 1;i <=n ;++i)
            bestr[i] = r[i];
    }
}

Ex5-21 罗密欧与朱丽叶

void search(int dep, int x, int y, int dir){

    if (dep == m*n-k && x==x1 && y==y1 && dirs <= best){
        if (dirs < best){
            best = dirs;
            count = 1;
            save();
        }
        else{
            ++count;
        }
        return;
    }
    if (dep==m*n-k || x==x1&&y==y1 || dirs>best) return;
    else{
        for (int i = 1;i <= 8;++i)
        if (stepok(x+dx[i], y+dy[i])){
            board[x+dx[i]][y+dy[i]] = dep+1;
            if (dir != i) ++dir;
            search(dep+1, x+dx[i], y+dy[i], i);
            if (dir != i) --dir;
            board[x+dx[i]][y+dy[i]] = 0;
        }
    }
}

bool stepok(int x, int y){
    return x>0 && x<=n && y>0 &&y<=m && board[x][y] = 0;
}

void save(){
    for (int i = 1;i <=n;++i)
    for (int j = 1;j < m;++j){
        bestb[i][j] = board[i][j];
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值