算法复习-算法实现题

1.把一个字符串(数组)进行逆序操作,如“abc"逆序为“cba” ,要求空间复杂度为O(1)。函数原型为:void  reverseString(char* str);

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

void reverseString(char* str)
{
	int len = strlen(str);
	for (int l = 0, r = len - 1; l < r; ++l, --r) {
		swap(str[l], str[r]);
	}
}

int main() 
{
	char s[109];
	scanf("%s", s);
	reverseString(s);
	printf("%s", s);
	
	return 0;
}

2.已知某数列符合规则:f(n)=f(n-1)+f(n-2)+f(n-3),且n为自然数,f(1)=1,f(2)=1,f(3)=1,那么请求第n个数是多少。要求时间复杂度为O(n),空间复杂度为O(1).

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

int f(int n)
{
	if (n <= 3) return 1;
	int a = 1, b = 1, c = 1, sum;
	for (int i = 4; i <= n; ++i) {
		sum = a + b + c;
		a = b;
		b = c;
		c = sum;
	}
	return c;
}

int main()
{
	int n;
	scanf("%d", &n);
	printf("%d", f(n));

	return 0;
}

3.最小子段和问题:给出一个序列,其子段为该序列的一个连续的区间。例如:(1 -1 2 2 -3 -3 2 -4) ,( -3 -3 2 -4)为他的一个子段。给出一个序列,求出数字求和最小的一个子段。例如上面的序列, (-3 -3 2 -4)就是一个最小的子段,数字的总和为-8。

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

const int INF = 0x3f3f3f3f; //任取一个极大值
int n;
int arr[109];
int sum[109]; //sum[i]为arr[i]的前缀和

int maxSubSum()
{
	int res = INF, maxsum = 0;
	for (int i = 1; i <= n; ++i) {
		maxsum = max(maxsum, sum[i]);
		res = min(res, sum[i] - maxsum);
	}
	return res;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &arr[i]);
		sum[i] = sum[i - 1] + arr[i];
	}
	printf("%d", maxSubSum());

	return 0;
}

4.键盘输入n个正整数(互不相等),找出其中和等于m的所有组合。输出顺序不限

【输入样例】输入:5(m的值) 1 2 3 4 5(n个正整数) 

【输出样例】:1  4 

                         2  3 

                          5 

#include<cstdio>
#include<vector>
using namespace std;

int n, m;
int arr[109];
vector<int> ans;

void DFS(int x,int sum) //x:当前arr下标  sum:当前和
{
	if (sum > m) return;
	if (x == n + 1) {
		if (sum == m) { //当前sum等于要求的m,则ans就是其中一个答案
			for (int i = 0; i < ans.size(); ++i) {
				printf("%d ", ans[i]);
			}
			printf("\n");
			exit(0); //找到一个子集直接退出程序(与第四题不同的点)
		}
		return;
	}
	//arr[x]选
	ans.push_back(arr[x]);
	DFS(x + 1, sum + arr[x]);
	ans.pop_back(); //回溯
	//arr[x]不选
	DFS(x + 1, sum);
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &arr[i]);
	}
	DFS(1, 0);

	return 0;
}

5.使用递归算法对一个整数进行逆序转换,如1234逆序为4321。顶层函数原型为:

   int reverseInteger(int a); 在具体实现时可增加子函数。

#include<cstdio>
using namespace std;

int num, ans;

int reverseInteger(int a)
{
	if (a == 0) return 0;
	ans = ans * 10 + a % 10;
	reverseInteger(a / 10);
	return ans;
}

int main()
{
	scanf("%d", &num);
	printf("%d", reverseInteger(num));

	return 0;
}

6.已知某数列符合规则:f(n)=2*f(n-1)+3*f(n-2)+f(n-3),且n为自然数,f(1)=1,f(2)=1,f(3)=1,那么请求第n个数是多少。要求时间复杂度为O(n),空间复杂度为O(1).

//跟第二题差不多
#include<iostream>
#include<cstring>
using namespace std;

int f(int n)
{
	if (n <= 3) return 1;
	int a = 1, b = 1, c = 1, sum;
	for (int i = 4; i <= n; ++i) {
		sum = a + 3 * b + 2*c; //跟第二题不同的地方
		a = b;
		b = c;
		c = sum;
	}
	return c;
}

int main()
{
	int n;
	scanf("%d", &n);
	printf("%d", f(n));

	return 0;
}

7.最大子段和问题:给出一个序列,其子段为该序列的一个连续的区间。例如:(1 -1 2 2 3 -3 4 -4) ,(2 2 3 -3 4)为他的一个子段。给出一个序列,求出数字求和最大的一个子段。例如上面的序列, (2 2 3 -3 4)就是一个最大的子段,数字的总和为8。

//跟第三题求最小字段和差不多
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF = 0x3f3f3f3f; //任取一个极大值
int n;
int arr[109];
int sum[109]; //sum[i]为arr[i]的前缀和

int minSubSum() //求最大字段和,不一样的是所有带'max'和带'min'的地方调换一下,就变成了相反的算法
{
	int res = -INF, minsum = 0;
	for (int i = 1; i <= n; ++i) {
		minsum = min(minsum, sum[i]);
		res = max(res, sum[i] - minsum);
	}
	return res;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &arr[i]);
		sum[i] = sum[i - 1] + arr[i];
	}
	printf("%d", minSubSum());

	return 0;
}

8.键盘输入n个正整数(互不相等),找出其中和大于m的所有组合。输出顺序不限

【输入样例】输入:5(m的值) 1 2 3 4 5(n个正整数) 

【部分输出样例】:1  5 

                                2  4 

                                3  5

//与第四题类似
#include<cstdio>
#include<vector>
using namespace std;

int n, m;
int arr[109];
vector<int> ans;

void DFS(int x, int sum) //x:当前arr下标  sum:当前和
{
	//if (sum > m) return; //(不同点1)
	if (x == n + 1) {
		if (sum > m) { //当前sum大于要求的m,则ans就是其中一个答案(不同点2)
			for (int i = 0; i < ans.size(); ++i) {
				printf("%d ", ans[i]);
			}
			printf("\n");
		}
		return;
	}
	//arr[x]选
	ans.push_back(arr[x]);
	DFS(x + 1, sum + arr[x]);
	ans.pop_back(); //回溯
	//arr[x]不选
	DFS(x + 1, sum);
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &arr[i]);
	}
	DFS(1, 0);

	return 0;
}

9.超级楼梯问题:有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级h或三级,要走上第M级,共有多少种走法?要求时间复杂度为O(n),空间复杂度为O(1)。

//动态规划,公式为第二题的公式:f(n)=f(n-1)+f(n-2)+f(n-3)。f(i)表示走到第i层的方案数
//代码与第二题完全一样,只是题目意思更加隐晦
#include<cstdio>
#include<vector>
using namespace std;

int m;

int f(int n) //第二题的f函数
{
	if (n <= 3) return 1;
	int a = 1, b = 1, c = 1, sum;
	for (int i = 4; i <= n; ++i) {
		sum = a + b + c;
		a = b;
		b = c;
		c = sum;
	}
	return c;
}

int main()
{
	int n;
	scanf("%d", &n);
	printf("%d", f(n));

	return 0;
}

10请用递归算法判断一个整数数组是否是对称的,比如数组为{1,2,2,1}就是对称的。要求递归函数返回bool类型。

#include<cstdio>
using namespace std;

int n;
int arr[109];

//原理:在C语言/C++中,true是1,false是0。当一串01串中只要出现一个0,那么这些数与(&)起来为0
//转换为该题题意就是,只要有一对对称位置的数不相同,那么这个数组就不对称
bool DG(int l,int r)
{
	if (l >= r) return true;
	bool res;
	//先比较当前arr中第l个和与之对称的第r个
	if (arr[l] == arr[r]) {
		res = true;
	}		
	else {
		res = false;
	}		
	res = (res & DG(l + 1, r - 1));
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &arr[i]);
	}
	printf("%d", DG(1, n));

	return 0;
}

11最大子段和问题:给出一个序列,其子段为该序列的一个连续的区间。例如:(1 -1 2 2 3 -3 4 -4) ,(2 2 3 -3 4)为他的一个子段。给出一个序列,求出数字求和最大的一个子段。例如上面的序列, (2 2 3 -3 4)就是一个最大的子段,数字的总和为8。

//与第七题重复了

12将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,其中n1≥n2≥…≥nk≥1,k≥1。正整数n的这种表示称为正整数n的划分。输出正整数n的不同划分个数及具体的划分形式。

例如正整数6有如下11种不同的划分:   

6;   

5+1;    

4+2,4+1+1;   

3+3,3+2+1,3+1+1+1;

2+2+2,2+2+1+1,2+1+1+1+1;   

1+1+1+1+1+1

//回溯法
#include<cstdio>
#include<vector>
#define ll long long
using namespace std;

const int INF = 0x3f3f3f3f;
int N;
vector<int> ans;
int cnt;

void DFS(int x, int sum)
{
	if (sum > N) return;
	if (sum == N) { //找到了其中一个划分,输出
		cnt++;
		for (int i = 0; i < ans.size(); ++i) {
			if (i == 0) printf("%d", ans[i]);
			else printf("+%d", ans[i]);
		}
		printf("\n");
	}
	for (int i = x; i >= 1; --i) {
		ans.push_back(i); //尝试将i作为一个划分中的元素
		DFS(i, sum + i); //递归去选择下一个元素
		ans.pop_back(); //回溯,去掉i这个元素,i这个位置将来会被替换成其它元素
	}
}

int main()
{
	scanf("%d", &N);
	DFS(N, 0);
	printf("共有以上%d种不同的划分", cnt);

	return 0;
}

13请用分支限界法实现:

八皇后问题:在8×8的国际象棋盘上,放置八个皇后,使任何一个皇后都不能吃掉另一个

国际象棋规则中,皇后可以吃到任何一个与他在同一行、同一列或者同一斜线上的敌方棋子,所以八皇后问题的所有解满足:

8个皇后都不在同一行、同一列,或者同一斜线上;或者任意行、列或者斜线上 有且仅有一个皇后。

#include<cstdio>
#include<iostream>
#include<string>
#define ll long long
using namespace std;

int l[10], vis[10][10]; //l[i]表示该列是否被占,vis[i][j]表示点(i,j)是否被占。1表示被占,0表示未占。
int cnt; //统计解的个数

void DFS(int x)
{
	if (x == 9) {
		cnt++;
		string s;
		for (int i = 1; i <= 8; ++i) {
			for (int j = 1; j <= 8; ++j) {
				if (vis[i][j]) {
					s += '0' + j;
				}
			}
		}
		cout << s << endl;		
		return;
	}
	for (int j = 1; j <= 8; ++j) {
		if (l[j] == 0) { //判断该列是否无皇后
			for (int k = 1; ; ++k) { //查看左上方这条线是否有皇后
				if (x - k < 1 || j - k < 1) break;
				if (vis[x - k][j - k]) {
					goto End; //有皇后
				}
			}
			for (int k = 1; ; ++k) { //查看右上方这条线是否有皇后
				if (x - k < 1 || j + k > 8) break;
				if (vis[x - k][j + k]) {
					goto End; //有皇后
				}
			}
			//如果左上右上两条线都没有皇后则该点可以放皇后
			l[j] = 1;
			vis[x][j] = 1;
			DFS(x + 1); //递归进入下一行
			//回溯,取消该点皇后
			l[j] = 0;
			vis[x][j] = 0;
		}
	End:;
	}
}

int main()
{
	DFS(1);
	printf("以上共有%d组解", cnt);

	return 0;
}

14给定一个高度为 n 的“数字三角形”,其中第 i 行(1<=i<=n)有 i 个数。(例子如下图所示)

1

2  3

4  5  6

7  8  9  10

初始时,你站在“数字三角形”的顶部,即第一行的唯一一个数上。每次移动,你可以选择移动到当前位置正下方或者当前位置右下方的位置上。即如果你在 (i,j)(表示你在第i行从左往右数第j个数上,下同),你可以选择移动到 (i+1,j) 或 (i+1,j+1)。

你想让你经过的所有位置(包括起点和终点)的数字总和最大。求这个最大值。

/*
	动态规划
	当在点(x,y)时,可以移动到点(x+1,y)或(x+1,y+1)
	那么对于点(i,j),可以从(i-1,j)或(i-1,j-1)移动过来,
	设f[i][j]表示移动到点(i,j)时的总和最大值,那么f[i][j] = max(f[i - 1][j], f[i - 1][j - 1]) + arr[i][j],此方程即为动态规划方程,
	最终答案为f[n][?]中的最大值
*/
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;

int n; //行数
int arr[109][109]; //arr[i][j]表示第i行第j列的值(j<=i)
int f[109][109];

int DP()
{
	//执行动态规划方程
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= i; ++j) {
			f[i][j] = max(f[i - 1][j], f[i - 1][j - 1]) + arr[i][j];
		}
	}
	//终点在最后一行,而最后一行有多个点,其中最大的f[n][j]即为所求答案
	int res = 0;
	for (int j = 1; j <= n; ++j) {
		res = max(res, f[n][j]);
	}
	return res;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= i; ++j) {
			scanf("%d", &arr[i][j]);
		}
	}
	printf("%d", DP());

	return 0;
}
/*
测试用例:
4
1
2 3
4 5 6
7 8 9 10
输出:
20
*/

15用回溯法解决:给定n个正整数wi和一个正整数m,在这n个正整数中找出一个子集,使得子集中的正整数之和等于m。

//类似于第四题,但这题只要求找到一个子集即可
#include<cstdio>
#include<vector>
using namespace std;

int n, m;
int arr[109];
vector<int> ans;

void DFS(int x, int sum) //x:当前arr下标  sum:当前和
{
	if (sum > m) return;
	if (x == n + 1) {
		if (sum == m) { //当前sum等于要求的m,则ans就是其中一个答案
			for (int i = 0; i < ans.size(); ++i) {
				printf("%d ", ans[i]);
			}
			printf("\n");
			exit(0); //输出一个子集后直接退出程序(与第四题不同的点)
		}	
		return;
	}
	//arr[x]选
	ans.push_back(arr[x]);
	DFS(x + 1, sum + arr[x]);
	ans.pop_back(); //回溯
	//arr[x]不选
	DFS(x + 1, sum);
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &arr[i]);
	}
	DFS(1, 0);

	return 0;
}

16给定一个存放整数的数组,重新排列数组使得数组左边为偶数,右边为奇数。

如对数组:1,2,3,4,5,6,7,8,9处理后,该数组变为:2,4,6,8,1,3,5,7,9。 

#include<cstdio>
#include<vector>
#define ll long long
using namespace std;

int n;
int arr[109];
vector<int> ans;

void reSort()
{
	for (int i = 1; i <= n; ++i) {
		if (arr[i] % 2 == 0) { //判断是否为偶数
			ans.push_back(arr[i]);
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (arr[i] % 2 != 0) { //判断是否为奇数
			ans.push_back(arr[i]);
		}
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &arr[i]);
	}
	reSort();
	for (int i = 0; i < n; ++i) {
		printf("%d ", ans[i]);
	}
	return 0;
}

17编写函数double getRestul(int n)并测试,它的功能是:根据以下公式计算结果S。

#include<cstdio>
#define ll long long
using namespace std;

double getRestul(int n)
{
	int flag = 1; //符号
	double res = 0; //结果
	for (int i = 1; i <= n; ++i) {
		res = res + flag * (1.0 / (2 * i - 1));
		if (flag == 1) flag = -1;
		else flag = 1;
	}
	return res;
}

int main()
{
	int num;
	scanf("%d", &num);
	printf("%lf", getRestul(num));
	
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值