算法自用复习

一、计算时间复杂度(master法)

二、回溯/递归

(1)子集树与排列树框架

子集树:
void Backtrack(int t) {     //t 表示当前是树的第t层,即对集合 S 中的第 t 个元素进行判断
    if (t > n)
        output(x);          //大于S中总的元素个数 ,遍历完成 
    else
        for (int i = 0; i < = 1; i++) {     // 两种可能 加入或者不加入到解集合 
            x[t] = i;
            if (Constraint(t) && Bound(t)){     //满足约数条件  
                    Backtrack(t + 1);           //对 t+1 层进行判断 
                } 
        }
}

排列树:
void Backtrack(int t) {     //t 表示集合 S 的第 t 个元素 
    if (t > n)
        output(x);
    else
        for (int i = t; i <= n; i++) {      //第t 个元素与其后面的所有元素进行交换位置 
            swap(x[t], x[i]);
            if (constraint(t) && bound(t)){ 
                    backtrack(t + 1);
                } 
            swap(x[t], x[i]);
        }
}

(2)递归求全排列(排列树)

#include <stdio.h>
#include <string.h>
int count = 0;
void swap(char* a, char* b)
{
	char temp;
	temp = *a;
	*a = *b;
	*b = temp;
}

void perm(char list[], int k, int m)
{
	if (k == m) //当循环到最后一层时(末尾),输出排列 
	{
		count++;
		printf("%s\n", list);
	}
	for (int i = k; i <= m; i++)
	{
		swap(&list[k], &list[i]);  //交换位置 
		perm(list, k + 1, m);        //递归调用 
		swap(&list[k], &list[i]);
	}
}
int main()
{
	char a[10];
	scanf("%s", a);
	int n = strlen(a);
	printf("排序序列为:\n");
	perm(a, 0, n - 1);
	printf("总排序个数为\n");
	printf("%d", count);
	return 0;
}

(3)子集和问题(子集树)

void Backtrack(int i){
    // 如果已经遍历到元素下标大于 n ,表示一个分支走完
    if(i>n){
        // 如果找到解,修改标志位
        if(sum==c)
            found=true;
        return;
    }
    // 如果未找到解
    if (found == false) {
        // 将当前元素加入和中
        sum+=a[i];
        // 记录当前元素已加入和中
        record[i]=true;
        // 继续递归遍历下一个元素
        Backtrack(i + 1);

        // 如果已经找到解,直接返回
        if (found == true)
            return;

        // 取出当前元素从和中减去
        sum-=a[i];
        // 记录当前元素未加入和中
        record[i]= false;
        // 继续递归遍历下一个元素
        Backtrack(i+1);
    }
}

int main(){
    // 读取输入
    cin>>n>>c;
    for (int i = 1; i <=n ; i++) {
        cin>>a[i];
    }
    // 调用 Backtrack 函数开始递归遍历
    Backtrack(1);
    // 输出结果
    for (int i = 1; i <=n ; i++) {
        if(record[i]== true)
            cout<<a[i]<<" ";
    }
    // 判断是否找到解
    if(found== false)
        cout<<"No Solution";
}

(4)快速排序

#include <iostream>
#include <algorithm>
using namespace std;
int a[1000];
int n;
void print()
{
    for(int i=1 ; i<=n ; i++)
    {
        cout<<a[i]<<' ';
    }
    cout<<endl;
}

int Partition(int a[],int low,int high)
{
    a[0]=a[low];
    while(low<high)
    {
        while(low<high&&a[high]>=a[0]) high--;
        a[low]=a[high];
        while(low<high&&a[low]<=a[0]) low++;
        a[high]=a[low];
    }
    a[low]=a[0];  //枢轴来到真正属于他的位置
    print();
    return low;
}

void QSort(int a[],int low,int high)
{
    if(low<high) /**记得加这个条件,否则将无限递归*/
    {
        int pivotloc=Partition(a,low,high);
        QSort(a,low,pivotloc-1);
        QSort(a,pivotloc+1,high);
    }

}



int main()
{
    cin>>n;
    for(int i=1 ; i<=n ; i++)
        cin>>a[i];
    QSort(a,1,n);
    return 0;
}

(5)归并排序

void Merge(int num[], int bg, int mid, int end)
{
    //建立临时数组存两个部分的合并后的数组
    int tmp[end - bg]= {0};
    int i = bg, j = mid;
    int k = 0;
    //比较两部分   当i=mid 或j=end 退出
    while (i < mid && j < end)
    {
        //由大小依次排序放到临时数组里   直到一个部分排完退出
        if (num[i] > num[j])
        {
            tmp[k++] = num[j++];
        }
        else
        {
            tmp[k++] = num[i++];
        }
    }
    //如果前半部分没存完  依次存进去
    while (i < mid)
    {
        tmp[k++] = num[i++];
    }
    //如果后半部分没存完  依次存进去
    while (j < end)
    {
        tmp[k++] = num[j++];
    }
    //把排好的临时数组存到num数组对应下标里
    for (int i = 0; i < k; i++)
    {
        num[bg++] = tmp[i];
    }
}

void MergeSort(int sourceArr[],  int startIndex, int endIndex) {
    int midIndex;
    if (startIndex < endIndex) {
        midIndex = startIndex + (endIndex - startIndex) / 2;//避免溢出int
        MergeSort(sourceArr,  startIndex, midIndex);
        MergeSort(sourceArr,  midIndex + 1, endIndex);
        Merge(sourceArr, startIndex, midIndex, endIndex);
    }
}

(6)第k小元素选择

int partition(int a[], int low, int high)
{
    a[0] = a[low];
    while (low < high)
    {
        while (low < high && a[high] >= a[0]) high--;
        a[low] = a[high];
        while (low < high && a[low] <= a[0]) low++;
        a[high] = a[low];
    }

    a[low] = a[0];
    return low;
}

//p 和 r 为序列的左右边界,k 为要查找的第 k 小元素
int Select(int a[], int p, int r, int k)
{
    if (p == r)
        return a[p];
    int i = partition(a, p, r);

    int j = i - p + 1;
    if (k <= j)
        return Select(a, p, i, k); // 对左段递归 填空1
    else
        return Select(a, i + 1, r, k - j); // 或者对右段递归 填空2
}

三、动态规划

(1)最长公共子序列

#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;

char s1[1001] ,s2[1001];
int dp[1001][1001];


int main()
{
    cin>>s1>>s2;
    int m=strlen(s1),n=strlen(s2);

    //int **dp=new int[m+1][n+1];

    int i,j;
    for(i=1 ; i<=m ; i++)
    {
        for(j=1 ; j<=n ; j++)
        {
            if(s1[i-1]==s2[j-1])
            {
                dp[i][j]=dp[i-1][j-1]+1;
            }
            else
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
    }
    //cout<<dp[i-1][j-1]<<endl;
    cout<<dp[m][n]<<endl;
    return 0;
}

(2)最大子段和

// 最大子段和.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//



#include <iostream>
using namespace std;


//末尾法,双层dp
//dp数组的含义:以i为结尾num[i]的最大连续子序列和
//dp[i]=max (dp[i-1] + num[i], num[i])
//舍弃负数,与贪心一样

/*
dp五部曲:
//dp含义
//递推公式
//初始化
//遍历顺序
//打印
*/

int num[101];
int dp[101];
int n;

int max_subarray_sum()
{
    int result = 0;
    dp[0] = num[0];
    for (int i = 1; i < n; i++)
    {
        /*遇到负数不用跳,继续计算?和之前的舍弃负数,不一样?
        if (num[i] < 0)
            continue;
        */

        dp[i] = max(dp[i - 1] + num[i], num[i]); //仅此处是关键,本题较简单

        //记录
        if (dp[i] > result)
            result = dp[i];
    }
    //return dp[n-1];  //注意返回的不是dp数组最后一个数,是最大的那个数 ※因为//dp数组的含义:以i为结尾num[i]的最大连续子序列和
    return result;
}


int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> num[i];
    }
    cout << max_subarray_sum() << endl;
}

(3)最大子矩阵和

int MaxSum(int n, int* num)
{
    //初始化dp
    int* dp = new int [n + 1];
    for (int i = 1; i <= n; i++)
        dp[n] = 0;

    //初始化
    int result = 0;
    dp[0] = num[0];

    //仅此步关键
    for (int i = 1; i < n; i++)
    {
        dp[i] = max(dp[i - 1] + num[i], num[i]);
    }

    //记录
    for (int i = 1; i < n; i++)
    {
        if (dp[i] > result)
            result = dp[i];
    }

    return result;

}

int maxSum2(int m, int n, int** a)
{
    int sum = 0;
    int* b = new int[n + 1]; //新建一个 b[ ]空间,用于保存条状二维矩阵纵向相加的结果
    for (int i = 1; i <= m; i++)
    {
        for (int k = 1; k <= n; k++)
            b[k] = 0; //初始化 填空1

        for (int j = i; j <= m; j++)
        {
            for (int k = 1; k <= n; k++)
                b[k] += a[j][k];  //累加  填空2

            int max = MaxSum(n, b);  //调用一维方法  填空3

            if (max > sum) 
                sum = max;
        }
    }
    return sum;
}

(4)最长递增子序列

// 最长递增子序列.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <algorithm>
using namespace std;


int dp[100];
int nums[100];
int n;

int solution()
{
    int result = 0;

    for (int i = 0; i < n; i++)
    {
        dp[i] = 1;
    }

    for (int i = 1; i < n; i++)
    {
        for (int j = 0; j < i; j++)
        {
            if (nums[i] > nums[j])
                dp[i] = max(dp[j] + 1, dp[i]); //不连续子序列    
            //注意这⾥不是要dp【i】 与 dp【j】 + 1进⾏⽐较,⽽是我们要取dp【j】 + 1的最⼤值。
        }

        //if (num[i] > num[i - 1])
        //    dp[i] = dp[i-1]+1; //连续子序列
    }

    for (int i = 0; i < n; i++)
    {
        if (dp[i] > result)
            result = dp[i];
    }

    return result;

}

int main()
{

    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> nums[i];
    }
    cout << solution() << endl;
}

(5)石子合并(区间dp)

//https://www.bilibili.com/video/BV1gz4y1y7Rv
#include <iostream>
#include <bits/stdc++.h>

using namespace std;

int n;
int a[110];
int s[110];
int f[110][110];


int main()
{
    cin>>n;
    memset(f,0x3f,sizeof(f));
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
        s[i]=s[i-1]+a[i]; //前缀和
        f[i][i]=0;
    }

//区间dp模板
    for(int len=2; len<=n; len++)
    {
        for(int l=1; l+len-1<=n; l++)
        {
            int r=l+len-1; //区间终点
            for(int k=l; k<r; k++) //决策:枚举分割点
            {
                f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
            }
        }
    }
    cout<<f[1][n]<<endl;
    return 0;
}


//矩阵连乘 矩阵链

(6)矩阵连乘

四、贪心问题

(1)区间相交问题

// 区间相交问题.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <algorithm>

using namespace std;

//在不知道贪心思路的情况下想的:并不是在输入的时候就处理,而是先排序后用贪心
//关键在于贪心:若有重叠,移除右边界大的那个区间,代码上体现在更新当前区间的右边界
int n;

struct MyStruct
{
    int s;
    int e;
}a[51];

int cmp(MyStruct s1, MyStruct s2)
{
    return s1.e < s2.e;
}

int solution()
{
    sort(a, a+n,cmp);
    
    int count = 1;
    int ccount = 0;

    //维护一个最新右边界 ,而不是与前一个比较, 因为可能有多个重叠
    int end = a[0].e;

    for (int i = 1; i < n; i++)
    {
        //if (a[i].s >= end)   //后一个区间的start >= 前一个区间的end:没重叠
        //{
        //    count++;
        //    end = a[i].e;
        //}

        //前面可能有多个,与当前i重叠的区间
        if (a[i].s < end)
            ccount++;
        else
            end = max(end, a[i].e);  //更新边界
    }

    //return n - count;  //最开始的题解:这里为什么?
    return ccount;
}




int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i].s >> a[i].e;
    }

    cout << solution() << endl;

}

(2)多机调度:贪心算法:最短处理时间优先,最长处理时间优先

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值