1.算法复杂度的求解与分析:
https://blog.csdn.net/weixin_43510131/article/details/84237929?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162519159816780265479319%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=162519159816780265479319&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v2~rank_v29-3-84237929.pc_v2_rank_blog_default&utm_term=%E7%AE%97%E6%B3%95%E5%A4%8D%E6%9D%82%E5%BA%A6%E5%88%86%E6%9E%90%E6%80%8E%E4%B9%88%E6%B1%82&spm=1018.2226.3001.4450
常用的时间复杂度所耗费的时间从小到大依次是:
O(1) < O(logn) < (n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
算法的空间复杂度的计算公式记作:S(n)=O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数,也是一种“渐进表示法”,这些所需要的内存空间通常分为“固定空间内存”(包括基本程序代码、常数、变量等)和“变动空间内存”(随程序运行时而改变大小的使用空间)
https://blog.csdn.net/qq_17534301/article/details/82872357?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
T(n)=O(f(n))
设语句1的频度为多少
依次类推 频度也是fn
然后Tn加起来 fn+起来 转化为O()
用常数1取代运行时间中的所有加法常数。 在修改后的运行次数函数中,只保留最高阶项。 如果最高阶项存在且不是1,则去除与这个项相乘的常数。
得到的最后结果就是大O阶
2.最长公共子序列
void LCSLength(int m,int n,char *x,char *y,int **c,int **b)
{
int i,j;
for(int i=1;i<=m;i++) c[i][0]=0;
for(int i=1;i<=m;j++) c[0][i]=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++){
if(x[i]=y[i])
{
c[i][j]=c[i-1][j-1];
b[i][j]=1;
}
else if(c[i-1][j]>=c[i][j-1]){
c[i][j]=c[i-1][j];
b[i][j]=2;
}
else{
c[i][j]=c[i][j-1]
b[i][j]=3;
}
}
void LCS(int i,int j,char *x,int **b)
{
if(i==0 ||j==0) return;
if(b[i][j]==1) {LCS(i-1,j-1,x,b)
else if(b[i][j]==2) LCS(i-1,j,x,b)
else LCS(i,j-1,x,b)
3.设计一个O(n2)的最长单调递增子序列的求解
#include<stdio.h>
int main() {
int max = 0, count = 1,n = 7;
int b, c;
int a[] = { 2,1,4,7,8,-1,20 };
for (int i = 0; i < n; i++) {
b = a[i];
for (int j = i + 1; j < n; j++) {
if (b < a[j]) {
b = a[j];
count++;
}
else break;
}
if (max < count) {
max = count;
c = i;
}
count = 1;
}
printf("%d ",a[c]);
b = a[c];
for (int i = c + 1; i < n; i++) {
if (b < a[i]) {
b = a[i];
printf("%d ", b);
}
else break;
}
return 0;
}
// Dynamic programming.
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length == 0) return 0;
int[] dp = new int[nums.length];
int res = 0;
Arrays.fill(dp, 1);
for(int i = 0; i < nums.length; i++) {
for(int j = 0; j < i; j++) {
if(nums[j] < nums[i]) dp[i] = Math.max(dp[i], dp[j] + 1);
}
res = Math.max(res, dp[i]);
}
return res;
}
}
4.设计一个O(nlogn)的算法,求最长公共子序列
class Solution {
public int lengthOfLIS(int[] nums) {
int[] tails = new int[nums.length];
int res = 0;
for(int num : nums) {
int i = 0, j = res;
while(i < j) {
int m = (i + j) / 2;
if(tails[m] < num) i = m + 1;
else j = m;
}
tails[i] = num;
if(res == j) res++;
}
return res;
}
}
5.编辑距离:
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符 删除一个字符 替换一个字符
for (int i = 0; i < dp.size(); i++) {
dp[i][0] = i;
}
for (int j = 0; j < dp[0].size(); j++) {
dp[0][j] = j;
}
for (int i = 1; i < dp.size(); i++) {
for (int j = 1; j < dp[i].size(); j++) {
dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = min(dp[i][j], dp[i - 1][j - 1]);
}
}
6.租用游艇
#include<iostream>
#include<cmath>
using namespace std;
int a[201][201],i,j,n,dp[201];
int main(){
cin>>n;
for(i=1;i<n;i++){
for(j=i+1;j<=n;j++)
cin>>a[i][j];
dp[i]=1e9;//初始化数组dp,使它
}
for(i=n-1;i>=1;i--)//跑n上流的中转站
for(j=i+1;j<=n;j++)//跑i下流的所有中转站
dp[i]=min(dp[i],a[i][j]+dp[j]);//记录
cout<<dp[1];
return 0;
}
7.字符串比较问题
对于长度相同的 2 个字符串 A 和 B,其距离定义为相应位置字符距离之和。2 个非空格字符的距离是它们的 ASCII
码之差的绝对值。空格与空格的距离为 0;空格与其它字符的距 离为一定值 k。在一般情况下,字符串 A 和 B 的长度不一定相同。字符串 A 的扩展是在 A 中插入若干 空格字符所产生的字符串。在字符串 A 和 B
的所有长度相同的扩展中,有一对距离最小的 扩展,该距离称为字符串 A 和 B 的扩展距离。 对于给定的字符串 A 和
B,试设计一个算法,计算其扩展距离。数据输入: 第 1 行是字符串 A;第 2 行是字符串 B。第 3 行是空格与其它字符的距离定值 k ————————————————
import java.util.Scanner;
/*
val(i,j): 字符串A和B的子串A[1..i]和B[1..j]的扩展距离
val(i,j)=min{val(i-1,j)+k, val(i,j-1)+k, val(i-1,j-1)+dist(ai, bj)}
*/
public class ZiFuChuanBiJiao {
private static String str1,str2;
private static int len1,len2;
private static int[][] val;
private static int k;//空格与其他字符的距离定值
private static int MAX = 100000;
public static void main(String[] args){
Scanner input = new Scanner(System.in);
while (true){
str1 = input.next();
str2 = input.next();
k = input.nextInt();
len1 = str1.length();
len2 = str2.length();
val = new int[len1+1][len2+1];
val[0][0] = 0;
comp();
}
}
private static void comp(){
int i,j,tmp;
for(i=0; i<=len1; i++)
for(j=0; j<=len2; j++)
if(i+j > 0){
val[i][j] = MAX;
if(i*j>0 && (tmp=(val[i-1][j-1])+dist(str1.charAt(i-1),str2.charAt(j-1))) < val[i][j])
val[i][j] = tmp;
if(i>0 && (tmp=(val[i-1][j])+dist(str1.charAt(i-1),' ')) < val[i][j])
val[i][j] = tmp;
if(j>0 && (tmp=(val[i][j-1])+dist(str2.charAt(j-1),' ')) < val[i][j])
val[i][j] = tmp;
}
System.out.println(val[len1][len2]);
}
private static int dist(char a, char b){
if(a ==' ' || b == ' ')
return k;
else
return Math.abs(a-b);
}
}
8.活动安排问题:
设有n个活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si
<fi 。如果选择了活动i,则它在半开时间区间[si, fi)内占用资源。若区间[si, fi)与区间[sj,
fj)不相交,则称活动i与活动j是相容的。也就是说,当si≥fj或sj≥fi时,活动i与活动j相容。
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
typedef struct{
int s;//活动开始时间
int f;//活动结束时间
int index;//活动序号,视情况而定是否用上
}action;
//根据活动结束时间进行非降序排序
void sort_action(action a[],int n)
{ int i,j;
action temp;
for(i=1;i<=n;i++)
for(j=1;j<=n-i;j++)
{
if(a[j].f>a[j+1].f)
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
int main()
{
int n;//输入n代表有n个活动
cin>>n;
action a[n+1];
for(int i=1;i<=n;i++){
cin>>a[i].s>>a[i].f;
a[i].index=i;//构造活动结构体数组
}
sort_action(a,n);//冒泡排序
bool b[n+1];
int num=1;
b[1]=true;//第一个活动必定安排
int pretime=a[1].f;
for(int i=2;i<=n;i++){
if(a[i].s >= pretime){
b[i]=true;
num++;//a[i]活动安排上
pretime=a[i].f;//更新pretime
}else{
b[i]=false;
}
}
//输出相容的最大活动数目
cout<<num<<endl;
//根据情况是否输出活动序号
for(int i=1;i<=n;i++){
if(b[i]) cout<<a[i].index<<" ";
}
return 0;
}
9.最小生成树
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mmax 100
#define maxt 100000000
int v[mmax][mmax];
void Prim(int n){
int low[mmax];//
int clo[mmax];
bool s[mmax];//判断该节点是否在S中
s[1]=true;
for(int i=2;i<=n;i++){
low[i]=v[1][i];//初始化
clo[i]=1;
s[i]=false;
}
for(int i=1;i<n;i++){//对剩余的n-1个顶点进行操作 ,此处的i并无实际意思,只是起到一个记录变量的作用
int min=maxt;
int j=1;
for(int k=2;k<=n;k++)//从V-S中找到S中边权最小的节点
if((low[k]<min)&&(!s[k])){
min=low[k];
j=k;
}
cout<<j<<" "<<clo[j]<<":"<<low[j]<<endl;
s[j]=true;
for(int k=2;k<=n;k++){//调整low中的值,加入j节点之后,使得两个集合中的距离最小
if((v[j][k]<low[k])&&(!s[k])){
low[k]=v[j][k];
clo[k]=j;
}
}
}
}
int main()
{
int n,m;//n vertexs and m edges
cin>>n>>m;
int ii,jj,tt;
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++)
v[i][j]=maxt;
}
for(int i=0;i<m;i++){
cin>>ii>>jj>>tt;
v[ii][jj]=tt;
v[jj][ii]=tt;
}
Prim(n);
return 0;
}
//6 10
//1 2 6
//1 4 5
//1 3 1
//2 3 5
//2 5 3
//3 4 5
//3 5 6
//3 6 4
//4 6 2
//5 6 6
krusal:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,tot=0,k=0;//n端点总数,m边数,tot记录最终答案,k已经连接了多少边
int fat[200010];//记录集体老大
struct node
{
int from,to,dis;//结构体储存边
}edge[200010];
bool cmp(const node &a,const node &b)//sort排序(当然你也可以快排)
{
return a.dis<b.dis;
}
int father(int x)//找集体老大,并查集的一部分
{
if(fat[x]!=x)
return father(fat[x]);
else return x;
}
void unionn(int x,int y)//加入团体,并查集的一部分
{
fat[father(y)]=father(x);
}
int main()
{
scanf("%d%d",&n,&m);//输入点数,边数
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].dis);//输入边的信息
}
for(int i=1;i<=n;i++) fat[i]=i;//自己最开始就是自己的老大 (初始化)
sort(edge+1,edge+1+m,cmp);//按权值排序(kruskal的体现)
for(int i=1;i<=m;i++)//从小到大遍历
{
if(k==n-1) break;//n个点需要n-1条边连接
if(father(edge[i].from)!=father(edge[i].to))//假如不在一个团体
{
unionn(edge[i].from,edge[i].to);//加入
tot+=edge[i].dis;//记录边权
k++;//已连接边数+1
}
}
printf("%d",tot);
return 0;
}
10.汽车加油问题:
一辆汽车加满油后可行驶n公里。旅途中有若干个加油站。设计一个有效算法,指出应
在哪些加油站停靠加油,使沿途加油次数最少。对于给定的n(n <= 5000)和k(k <= 1000)个加油站位置,编程计算最少加油次数。并证明算法能产生一个最优解。
要求:
输入:第一行有2个正整数n和k,表示汽车加满油后可行驶n公里,且旅途中有k个加油站。接下来的1 行中,有k+1 个整数,表示第k个加油站与第k-1 个加油站之间的距离。第0 个加油站表示出发地,汽车已加满油。第k+1 个加油站表示目的地。
输出:输出编程计算出的最少加油次数。如果无法到达目的地,则输出”NoSolution”。
思路:
汽车行驶过程中,应走到自己能走到并且离自己最远的那个加油站,在那个加油站加油后再按照同样的方法贪心
具体算法:
先检测各加油站之间的距离,若发现其中有一个距离大于汽车加满油能跑的距离,则输出no solution
否则,对加油站间的距离进行逐个扫描,并且累加记录距离,尽量选择往远处走,不能走了,即当累加距离大于n时,就加油一次(num++),最终统计
#include <iostream>
using namespace std;
void greedy(int d[],int n,int k) {//汽车加满油之后可以行驶n千米,沿途有k个加油站
int num = 0;
for(int i = 0;i <= k;i++) {
if(d[i] > n) {//加油站之间的距离
cout<<"no solution\n"<<endl;
break;
}
}
for(int i = 0,sum = 0;i <= k;i++) {
sum += d[i];//加油站间距离相加
if(sum > n) {//车无法达到第i个加油站,加油一次,s等于从加过油的地方开始计
num++;
sum = d[i];
}
}
cout<<num<<endl;
}
int main() {
int i,n,k;
int d[1000];
cout<<"请输入汽车加满油可行驶千里数以及加油站数量"<<endl;
cin>>n>>k;
cout<<"输入第k个加油站和第k-1个加油站之间的距离"<<endl;
for(i = 0; i <= k; i++)
cin>>d[i];
greedy(d,n,k);
}
11.批处理作业调度
12.n皇后问题
130页
非递归的意思差不多是一个一个格子+0走的
13.0-1背包
132