---恢复内容开始---
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); }
快速排序:
--- 这里有一个结合快速排序思想,计算第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的下标; */
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)); }
// 人数=2^n,这里输入n即可。
8.3.4非线性方程求根: ---- 转换为二分枚举判断。
//前提: 方程有单调的特性。

while(y-x>1e-5){ double m=x+(y-x)/2; ... ... }
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中的堆,来进行弹出最小值获取每次需要进行合并的两个 权值 最小的结点。