算法竞赛入门经典---函数部分3

---恢复内容开始---

第八章:高效算法设计

8.1算法分析初步:

理解时间复杂度的估算、掌握一些简单的优化算法时间的技巧。

例题8.1:最大连续和:

时间复杂度:  O(n^3)--->O(n^2)--->O(nlog(n))--->O(n)

O(n^3):   最平常的模拟枚举办法;

O(n^2):   我们可以预处理前缀和,之后改进一个二重循环为一重循环,降低时间复杂度;

O(nlog(n)):   通过分治法,进一步降低时间复杂度;

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <iostream>
#include <algorithm>
using namespace std;
int f[1010];
int maxnum(int a,int b){ //求a-b之间的连续最大和 
    if(b-a==1) return f[a];
    else{
         //划分+递归求解子问题 
         int mid=a+(b-a)/2;
         int t=maxnum(a,mid) > maxnum(mid,b) ? maxnum(a,mid) : maxnum(mid,b);     
         
         //合并子问题 :得到起点在左边,终点在右边的和最大连续子序列; 
         int L,R;int v=0;L=0; 
         for(int i=mid-1;i>=a;i--){v=v+f[i];if(v>L) L=v;}
         v=0;R=0;
         for(int j=mid;j<b;j++){v=v+f[j];if(v>R) R=v;}
         return L+R > t ? L+R:t;
    }
}
//递归分治求解; 
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    int i,j,k,n;
    scanf("%d",&n);
    for(i=0;i<n;i++) scanf("%d",&f[i]);
    int ans=maxnum(0,n);
    printf("%d\n",ans);
    return 0;    
}

 O(n):    通过理解题目含义,我们需要计算最大的连续和,求当J确定时,S[j]-S[i] 最大值(i<j),相当于求S[i]最小,我们在这里维护一个目前为止遇到过的最小S即可。

8.2再谈排序与检索:

归并排序:

int f[N]; 
void Print(int n){
    for(int i=0;i<n;i++){
        printf("%d ",f[i]);
    }printf("\n");
}
void Sort(int *A,int x,int  y){//A是辅助数组 
    int i,j,k;
    if(y-x>1){
        //划分子问题;
        int m=x+(y-x)/2;
        //计算子问题 
        int p=x,q=m;i=x;
        Sort(A,x,m);Sort(A,m,y);
        //合并子问题 
        while(p<m || q<y){
            if(q>=y || (p<m && f[p]<=f[q])) A[i++]=f[p++];
            else A[i++]=f[q++];
        } 
        for(i=x;i<y;i++) f[i]=A[i]; 
    }
}
//分治法,归并排序
void text(){
    int i,j,k,n;scanf("%d",&n);
    for(i=0;i<n;i++) scanf("%d",&f[i]);
    int A[N];memset(A,0,sizeof(A));
    Sort(A,0,n);//区间左闭右开;
    
    Print(n);
}
View Code

快速排序:

--- 这里有一个结合快速排序思想,计算第k小数的 n*log(n)时间复杂度的算法。

二分查找: --- 这里用到了STL中的 sort 、lower_bound 、 upper_bound 三个函数。

用法实例:

void text(){ 
    int i,j,k,n;scanf("%d",&n);
    for(i=0;i<n;i++) scanf("%d",&f[i]);
    //int A[N];memset(A,0,sizeof(A));
    int t;scanf("%d",&t);
    printf("%d\n",upper_bound(f,f+n,t)-lower_bound(f,f+n,t)) ;
    
    //Print(n);
}
/*
前提: 数组排好序 
其中lower_bound(v,v+n,b) //在数组v的前n个数字中查找数字b,返回第一个数字b的下标;
其中upper_bound(v,v+n,b) //在数组v的前n个数字中查找数字b,返回最后一个数字b的下标; 

*/
View Code

 

8.3 递归与分治:

8.3.1 棋盘覆盖问题

 

8.3.2 循环日程表问题:

// 分治的经典问题:

int f[N][N]; 
void Print(int n){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++) 
            printf("%d ",f[i][j]);
        printf("\n");
    }
}
void Pai(int n,int x){
    int i,j;
    if(n>1){
        //递归解决子问题; 
        int t=(int)pow(2,n-1);
        Pai(n-1,t);
        Pai(n-1,t);
        //合并 ;
        for(i=t+1;i<=x;i++)
            for(j=t+1;j<=x;j++)
                f[i][j]=f[i-t][j-t]; //右下角;
        for(i=1;i<=t;i++)
            for(j=t+1;j<=x;j++)
                f[i][j]=f[i][j-t]+t; // 右上角 
        for(i=t+1;i<=x;i++)
            for(j=1;j<=t;j++)
                f[i][j]=f[i-t][j]+t; // 左下角 
    }
    else{
        f[1][1]=f[2][2]=1;
        f[1][2]=f[2][1]=2;
    }
}
// 递归与分治: 循环日程表问题! 
void text(){ 
    int i,j,k,n;scanf("%d",&n);//共2^n支球队;
    Pai(n,(int)pow(2,n));     
    
    
    Print((int)pow(2,n));
}
View Code

// 人数=2^n,这里输入n即可。

8.3.4非线性方程求根:  ----   转换为二分枚举判断。

//前提: 方程有单调的特性。   

while(y-x>1e-5){
     double m=x+(y-x)/2;
     ...
     ...  
}
View Code

8.3.4 最大值最小化  --- 是一种很常见的优化问题! 将枚举问题转化为 --- 判定问题。(通过枚举所有S(i)均不超过的 x 来进行)

 

8.4贪心法:

8.4.1 最优装载问题

8.4.2 部分背包问题

8.4.3 乘船问题

--- 以上都是很明显的贪心算法问题

8.4.4 选择不想交区间 + 8.4.5 区间选点问题 

// 按照区间后端点排序

8.4.6 区间覆盖问题 

// 每一步进行之前,都要进行一次截断区间,重新排序问题。 这里按照 区间前端点排序,如果起点不是s,无解;否则在起点是s的区间中选择b最大的区间。依次进行下去

8.4.7 Huffman编码

// 前缀码,贪心算法。

--- 技巧: 这里可以使用STL中的堆,来进行弹出最小值获取每次需要进行合并的两个 权值 最小的结点。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值