【c++】算法设计与分析(保姆级!题目解析+答案)

【c++】算法设计与分析(保姆级!题目解析+答案)

算法类型

在这里插入图片描述

一. 分治法

可使用分治法求解的一些经典问题,可以在目录里看自己要哪些,都是先给代码,在说解析。

(1)二分搜索
(2)大整数乘法
(3)Strassen矩阵乘法
(4)棋盘覆盖
(5)归并排序
(6)快速排序
(7)线性时间选择
(8)最接近点对问题
(9)循环赛日程表
(10)汉诺塔

1. 二分搜索(太简单了,所以一定要会)

#include<iostream>usingnamespace std;#defineMAX10int a[MAX]={0,22,23,33,36,55,56,58,89,94};intSearch_two(int n){int left =0;int right = MAX-1;int mid =0;while(left<=right){ mid =(left+right)/2;if(n < a[mid]){ right = mid -1;}elseif(n > a[mid]){ left = mid +1;}elsereturn mid;}return-1;}intmain(){int searchN =0; cin>>searchN;int result =Search_two(searchN);if(result ==-1) cout <<"no find";else cout <<"yes,its "<<a[result]<<" in "<< result;return0;}

思路:
第一遍:

在这里插入图片描述


第二遍:

在这里插入图片描述

2. 循环赛日程表(不如叫矩阵复制)

#include<iostream>#include<cmath>#defineMAX100int a[MAX][MAX]={0};usingnamespace std;voidCOPY(int tox,int toy,int fromx,int fromy,int r){for(int i =0; i<r ;i++){for(int j =0; j<r ; j++){ a[tox+i][toy+j]= a[fromx+i][fromy+j];}}}voidTable(int k){int n =pow(2,k);int i , j , r=0;for(i =0; i < n; i++){ a[0][i]=i+1;}for(r =1; r < n; r*=2){for(j =0; j< n; j = j +2* r){COPY(r,r+j,0,j,r);COPY(r,j,0,r+j,r);}}}intmain(){int k; cin>>k;int n =pow(2,k);Table(k);for(int i =0; i<n ; i++){for(int j =0; j<n ; j++){ cout<<a[i][j]<<" ";} cout<<endl;}return0;}

思路:
赛程表满足一个规律:
左上角 右上角
左下角 右下角

可以发现对角的数值是相等的。

在这里插入图片描述

这里的COPY函数,可以看成互换函数,也就是把一个小矩阵里的四个数字互换:
交换的是左上右下和右上左下,
所以需要两个COPY函数:
COPY(r,r+j,0,j,r);
COPY(r,j,0,r+j,r);

在这里插入图片描述


可以看到第一个COPY复制的是左上角的。

在这里插入图片描述


在这里插入图片描述

3. 快速排序

#include<iostream>usingnamespace std;#defineMAX10int a[MAX]={37,89,23,58,36,55,56,22,34,9};voidquicksort(int start,int end){if(start < end){int left = start;int right = end;int temp = a[start];while(left < right){//从右边开始,要把右面小的换过去while(left < right && a[right]> temp){ right --;}//右面小了就交换 a[left]= a[right];while(left < right && a[left]< temp){ left ++;} a[right]= a[left];} a[left]= temp;quicksort(start,left-1);quicksort(right+1,end);}}intmain(){quicksort(0,9);//打印数组for(int i =0;i<10;i++){ cout<<a[i]; cout<<" ";}return0;}

原理是:保留一个空位,找到那个我们选的数字在数组中的位置。

在这里插入图片描述


![在这里插入图片描述](https://i-blog.ZEEKLOGimg.cn/direct/8788682132734380a490973996006e06.png

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


此时选择的数字左面的数字都比他小,右面的数都比他大。
接着递归左面的数字和右面的数字。

4. 斐波那契

#include<iostream>usingnamespace std;intf(int n){if(0== n)return0;if(1== n)return1;returnf(n-1)+f(n-2);}intmain(){int n; cout<<"cin n"; cin >> n;int re =f(n); cout<< re;return0;}

二. 回溯

1. n皇后

代码如下:

#include<iostream>#include<cmath>usingnamespace std;int n;int pos[100];int totall;boolissafe(int r,int c){for(int i =0; i < r; i++)if(pos[i]== c ||abs(pos[i]- c)==abs(i - r))returnfalse;returntrue;}voiddfs(int r){if(r==n) totall++;for(int i =0; i < n;i++){if(issafe(r,i)){ pos[r]= i;dfs(r +1);}}}intmain(){ cout <<"Enter N: "; cin >> n; totall =0;dfs(0); cout <<"Total solutions for "<< n <<"-Queens: "<< totall << endl;return0;}

原理:
1.我们首先要知道的数学知识:
横坐标之差的绝对值等于纵坐标之差的绝对值
abs(列差) == abs(行差)
假设有一个4*4的表格:

在这里插入图片描述


我们选取几个位置,

pos(2,2)和pos(3,3),可以观察发现
行差:3-2=1
列差:3-2=1

pos(0,3)和pos(2,2),可以观察发现
行差:2-0=2
列差:3-2=1

在这里插入图片描述


所以在n皇后的代码里,我们就可以这么判断是否在一条斜线上。abs(pos[i] - c) == abs(i - r);
公式翻译过来就是:
|旧列 - 新列| == |旧行 - 新行|

2.代码解析
关键数组:pos[r] =c;这个数组很巧妙用了一个一维数组来记录了二维的棋盘,减少了空间的使用。例如pos[2]=3 表示在第三行的皇后在第四列。这里的pos[2] 是列,2是行。

为什么if(r==n)?
totall++;
是因为如果遍历完一次完整的棋盘,r就会等于n。

三.贪心算法

贪心算法就是每次取最好的结果。
所以首先都要排序,把结果好的放前面,开始遍历。

1. 背包问题

#include<iostream>#include<algorithm>usingnamespace std;typedefstructBOX{int id;float w;float v;float r;}BOX;boolcmp(BOX a,BOX b){return a.r > b.r;}voidbeibao(float weight,int n,BOX a[]){float sumvalue=0;for(int i=0; i<n ;i++){if(weight >0&& weight >= a[i].w){ weight = weight - a[i].w; sumvalue += a[i].r; cout<<"装入比例100%"<< endl;}else{float bili = weight / a[i].w ; sumvalue = sumvalue +(a[i].r * bili); cout<<"装入比例"<< bili << endl; weight =0;}} cout <<"总价值为"<<sumvalue<<endl;}intmain(){// === 这里是直接定义好的数据,想改就在这里改 ===// 1. 定义背包总容量float weight =50.0;// 2. 定义物品数据:{id, 重量, 价值, 0} // 注意:最后一个0是占位符,因为性价比r还没算,下面会自动算 BOX a[]={{1,30,60,0},// 这里的a[0]是占位用的,因为你的逻辑是从下标1开始{2,10,60,0},// 物品1:重10,值60{3,20,100,0},// 物品2:重20,值100{4,30,120,0}// 物品3:重30,值120};// 自动计算物品个数(减去第0个占位符)int n =sizeof(a)/sizeof(a[0]);// ============================================ cout <<"=== 背包问题自动运行 ==="<< endl; cout <<"背包总容量: "<< weight << endl; cout <<"物品总数: "<< n << endl << endl;// 自动计算性价比 rfor(int i =0; i < n; i++){ a[i].r = a[i].v / a[i].w;// 顺便打印一下初始数据,让你看清楚 cout <<"物品"<< a[i].id <<" -> 重量:"<< a[i].w <<" 价值:"<< a[i].v <<" 单价:"<< a[i].r << endl;} cout <<"----------------------"<< endl;// 排序sort(a,a+n,cmp);//beibaobeibao(weight,n,a);return0;}

核心就是先排序再遍历,如果能装下全部就装,不能去全装下就取最多的能装下的装,所以贪心没办法用于0-1背包(也就是物品无法被”切开“的情况)

四. 动态规划

因为没办法用贪心解决0-1背包,所以用动态规划。
动态规划不同于递归,有点像把好的情况存起来,每次都比较放入新的好情况。

1. 0-1背包

//核心代码#include<iostream>#include<algorithm>#include<string>usingnamespace std;#defineMAX10int v[MAX];int dp[MAX][MAX];int w[MAX];intmain(){int n=3,c=5;int j = c;for(int i =1; i<=n;i++){for(j =0; j<= c ; j++){ dp[i][j]= dp[i-1][j];if(j >= w[i]){ dp[i][j]=max(dp[i][j], dp[i-1][j-w[i]]+ v[i]);}}} j = c;for(int i = n; i >=1; i--){if(dp[i][j]> dp[i-1][j]){ cout << i; j = j - w[i];}}return0;}
//可运行代码#include<iostream>#include<algorithm>#include<string>// 引入string头文件,方便打印名字usingnamespace std;// 定义物品最大数量和背包最大容量,稍微开大一点constint MAX_N =100;constint MAX_C =1000;int w[MAX_N];// 重量int v[MAX_N];// 价值 string names[MAX_N];// 物品名字(为了让你看清楚选了啥)int dp[MAX_N][MAX_C];// DP表格intmain(){// === 这里直接写死你的例子 ===int n =3;// 物品数量int c =6;// 背包容量// 第1个物品:葡萄 (重量2, 价值3) names[1]="葡萄"; w[1]=2; v[1]=3;// 第2个物品:矿泉水 (重量3, 价值5) names[2]="矿泉水"; w[2]=3; v[2]=5;// 第3个物品:西瓜 (重量4, 价值6) names[3]="西瓜"; w[3]=4; v[3]=6;// ===========================// 核心算法部分for(int i =1; i <= n; i++){for(int j =0; j <= c; j++){ dp[i][j]= dp[i -1][j];if(j >= w[i]){ dp[i][j]=max(dp[i][j], dp[i -1][j - w[i]]+ v[i]);}}}// 输出结果 cout <<"=== 运行结果 ==="<< endl; cout <<"最大价值: "<< dp[n][c]<< endl; cout <<"选中物品: ";int j = c;for(int i = n; i >=1; i--){if(dp[i][j]> dp[i -1][j]){// 这里多打印了个名字,让你看得更清楚 cout << names[i]<<"("<< i <<") "; j = j - w[i];}} cout << endl;return0;}

一句话解释就是,首先要考虑放不放的下,
如果放得下的话要考虑放他划算还是不放划算。

这也就是代码里的思想max( dp[i-1][j] , dp[i-1][j - w[i]] + v[i])
j是目前的最大容量。
i则表示当前的物品。所以i从1开始遍历。
j-w[i]就是目前的最大容量减去当前的物体的重量。
分辨的 前者是:没装他
后者是:装了他加上他的价值

值得注意的地方:这里的遍历i是从1开始的,因为这样在算i-1的时候就不会越界,所以下一题也是同理。

2. 最长公共子序列

//可运行代码#include<iostream>#include<algorithm>#include<string>// DP 数组稍微开大一点,防止越界#defineMAX100usingnamespace std;int dp[MAX][MAX];// 全局数组自动初始化为0,省心!intmain(){ string s1, s2;// 让我们输入两个字符串来测试一下! cout <<"请输入第一个字符串: "; cin >> s1; cout <<"请输入第二个字符串: "; cin >> s2;int n = s1.length();// s1 的长度int m = s2.length();// s2 的长度(注意:两个字符串长度可能不一样!)// 核心逻辑开始!for(int i =1; i <= n; i++){for(int j =1; j <= m; j++)// 这里是 j <= m,不是 n 哦!{if(s1[i-1]== s2[j-1]){// !!!一定要 +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[n][m]<< endl;return0;}
//简单代码intmain(){for(int i =1;i <= n; i++){for(int 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]);}}}return0;}

首先,要遍历两个字符串,
同时,判断尾巴上的字符一不一样,
如果一样:说明长度刚好就是(两个字符去掉尾巴+1)的长度
如果不一样:选择把s1的尾巴扔掉和s2的尾巴扔掉
所以有一个max(dp[i-1][j],dp[i][j-1])

Read more

C++ 模板进阶:特化、萃取与可变参数模板

C++ 模板进阶:特化、萃取与可变参数模板

C++ 模板进阶:特化、萃取与可变参数模板 💡 学习目标:掌握模板进阶技术的核心用法,理解模板特化的深层应用、类型萃取的实现原理,以及可变参数模板的灵活使用,提升泛型编程的实战能力。 💡 学习重点:模板特化的进阶场景、类型萃取工具的设计与应用、可变参数模板的展开技巧、折叠表达式的使用方法。 一、模板特化进阶:处理复杂类型场景 💡 模板特化不只是针对单一类型的定制,还能处理指针、引用、数组等复杂类型,实现更精细的类型适配逻辑。 1.1 指针类型的模板特化 通用模板默认处理普通类型,我们可以为指针类型单独编写特化版本,实现指针专属的逻辑。 #include<iostream>#include<string>usingnamespace std;// 通用模板:处理普通类型template<typenameT>classTypeProcessor{public:staticvoidprocess(T data){ cout

By Ne0inhk
【Java 学习】Comparable接口 和 Comparator接口,掌控排序逻辑解析,深入 Comparable 和 Comparator 的优雅切换

【Java 学习】Comparable接口 和 Comparator接口,掌控排序逻辑解析,深入 Comparable 和 Comparator 的优雅切换

💬 欢迎讨论:如对文章内容有疑问或见解,欢迎在评论区留言,我需要您的帮助! 👍 点赞、收藏与分享:如果这篇文章对您有所帮助,请不吝点赞、收藏或分享,谢谢您的支持! 🚀 传播技术之美:期待您将这篇文章推荐给更多对需要学习Java语言、低代码开发感兴趣的朋友,让我们共同学习、成长! 1. Comparable接口 1.1 为什么要使用Comparable接口 先看代码两组代码: 代码1: importjava.util.Arrays;publicclassMain{publicstaticvoidmain(String[] args){// 创建一个数组String[] strs ={"李华","小明","小红"};// 对数组进行排序Arrays.sort(strs);// 打印System.out.println(Arrays.toString(strs));}} 上述代码可以打印出比较的后的顺序。

By Ne0inhk
全栈开发的演变:从LAMP到MEAN再到现代JavaScript

全栈开发的演变:从LAMP到MEAN再到现代JavaScript

全栈开发者概述 在众多企业中,尤其是创业型公司,人力资源部门在招聘时常常渴望能够找到一位技术上的多面手,即全栈开发者。那么,究竟什么是全栈开发者,他们需要掌握哪些核心技能呢? ◆ 定义与技能要求 传统上,“全栈”开发人员被界定为既能够胜任前端开发,也能进行后端开发工作。然而,在现代软件开发领域,全栈开发者的能力已经超越了这一传统定义。他们不仅需要掌握传统的开发技能,还需要熟悉DevOps工具和技术,如Git、测试以及网站部署等。由于“栈”这一概念涵盖了这些广泛的技术领域,因此,全栈开发人员可被理解为在构建网站的过程中,能够独当一面地处理所有技术问题。 ◆ 技术栈的发展 这些年来,随着技术的发展,某些“栈”已经逐渐淡出人们的视线。其中,LAMP栈(Linux、Apache、MySQL和PHP的组合)曾一度备受瞩目。掌握这四项技术的开发者,被视为能够独立处理网站构建中的各项技术问题。然而,随着时代的演变,全栈的概念已经超越了单纯的技能掌握。 ◆ 角色的变化 LAMP栈的全栈开发人员,确实需要精通Linux、Apache、MySQL和PHP,这些技术构成了网站构建的基础

By Ne0inhk

如何在5分钟内用JavaScript创建专业级韦恩图:venn.js终极指南

如何在5分钟内用JavaScript创建专业级韦恩图:venn.js终极指南 【免费下载链接】venn.jsArea proportional Venn and Euler diagrams in JavaScript 项目地址: https://gitcode.com/gh_mirrors/ve/venn.js 想要在网页上快速创建面积比例准确的韦恩图和欧拉图吗?venn.js是专为数据可视化设计的JavaScript库,让您能够轻松生成专业级的集合关系图表。无论您是数据分析师、前端开发者还是科研工作者,这个强大的工具都能帮助您直观展示数据间的交集和并集关系。🚀 为什么选择venn.js进行数据可视化? venn.js作为专业的JavaScript韦恩图库,具有以下核心优势: * 智能布局算法:自动计算最合适的圆环位置和大小 * 面积比例准确:确保每个区域的面积与数据量成正比 * 完全交互式:支持鼠标悬停、点击等交互操作 * 高度可定制:颜色、样式、文字等均可自由配置 * 轻量且高效:基于D3.js构建,性能优越 快速开始:

By Ne0inhk