小米OJ # 21题 按序组成最大的数

小米OJ前3页所有题中通过率最低的一道,也确实花费很长时间调试程序。在这里插入图片描述
题目: 按序组合成最大的数
描述:
给定两个数组,由数字 0-9 组成的,长度分别为 a 和 b,需要用 a、b 两个数组中的数字组合得到长度为 k (k <= a+b)的正整数,输出所有可能的组合中数值最大的一个(原同一数组中的数字顺序不能改变)
输入:
a、b 数组中的数字间用 , 隔开,两个数组以及 k 之间用空格隔开,如:1,3,4,5,1,2 8,9,1 5,其中 a = [1,3,4,5,1,2],b = [8,9,1],k = 5. 数组 a, b 元素个数不大于20.
输出:
长度为 k 的所有组合中数值最大的整数,如:95121
从 a 或 b 中取数的时候需保证 a,b 内部的顺序不变,如第一个数取到 b 中的 9,那么 b 中只有 1 可以后续取用;第二个数取到 a 中的 5,则 a 中还剩下 1,2 可以后续取用。

直接先给出我自己的解题想法:
首先读入数据,分成两个数组(N1为数组a的长度,N2位数组b的长度),方便后面数据读取使用。

  1. 首先判断是否N1+N2==k,如果是,直接从头开始比较大小选数,这种情况很简单
  2. 如果不是(N1+N2>k或N1+N2<k),则进入递归函数:
    • 首先给出递归的边界条件,确定什么时候跳出递归。
    • 写一个函数,检测当前选到a[weizhi1]和b[weizhi2]的时候,后面所剩下的所有数字够不够组成题目所要求的k。
int isok(int weizhi1, int weizhi2, int k) {
   if (N1 - weizhi1 + N2 - weizhi2 >= k) return 1;
   else return 0;
}
  • 检测总数number=k,如果在递归的过程中number<=0,则返回0;
  • 为了避免读取数组出现越界的情况,同时还要检测是否weizhi1和weizhi2大于等于N1和N2;
  1. 写好边界条件后,从a,b数组开始读取数据。比较MAX(a[i],b[j]),谁大选谁;
  2. 如果出现a[i]=a[j]的情况,那就两个都选,选好后保存两种情况下的函数返回值,然后做一个比较。将最大值赋给返回值。
  3. 选好当前值后,weizhi1和weizhi2对应往后移动一位(选谁移动谁),再次进入递归函数。

程序:

#include<stdio.h>  
#include<string.h>
#define MAX(a,b) (a)>(b)?(a):(b);
int isok(int weizhi1, int weizhi2, int k);
char* back_findMAX(int weizhi1, int weizhi2, int number);
int num = 0, N1 = 0, N2 = 0, a1[20] = { 0 }, a2[20] = { 0 };
//long long int value = 0; //不能用整数作为递归的返回值,后面测试数据很长,超过了_int64的存储范围
char* p;       //利用指针作为函数的返回值

//处理N1+N2=k的情况下的选数问题
static int task1(const int A1[], const int A2[], const int N1, const int N2) {
	long  int tmp = 0;    //这里应该改成long long int或者用指针*p来存,防止数字过大的情况,但我懒的改了
	int i;
	for (i = 0; i < N1 && i < N2; i++) {
		if (A1[i] >= A2[i]) tmp = (tmp * 10 + A1[i]) * 10 + A2[i];
		else tmp = (tmp * 10 + A2[i]) * 10 + A1[i];
	}
	if (i == N1 && i < N2) {
		for (; i < N2; i++) {
			tmp = tmp * 10 + A2[i];
		}
	}
	else if (i == N2 && i < N1) {
		for (; i < N1; i++) {
			tmp = tmp * 10 + A1[i];
		}
	}
	return tmp;
}

int main()
{
	char input[200];
	gets_s(input);
	char* p1, * p2, * p3;

	p1 = strtok(input, " ");
	p2 = strtok(NULL, " ");
	p3 = strtok(NULL, " ");
	int i = 0, j = 0;
	for (i = 0, j = 0; i < strlen(p1); i++) {
		if (*(p1 + i) != ',') {
			a1[j++] = *(p1 + i) - '0';
		}
	}
	N1 = j;
	for (i = 0, j = 0; i < strlen(p2); i++) {
		if (*(p2 + i) != ',') a2[j++] = *(p2 + i) - '0';
	}
	N2 = j;
	for (int i = 0; i < strlen(p3); i++) {
		num = num * 10 + *(p3 + i) - '0';
	}
	p = (char*)malloc(40 * sizeof(char)); //直接申请最大的40,也可以根据num的大小进行申请,
	if (p == NULL) {
		printf("error");
		return 0;
	}
	else {
		memset(p, 0, 40 * sizeof(char));           //将所有的申请到的内存全部初始化为0
	}

	//读完数据
	if (N1 + N2 == num) printf("%d", task1(a1, a2, N1, N2));
	else  {
		printf("%s", back_findMAX(0, 0, num));
	}
	return 0;
}

int isok(int weizhi1, int weizhi2, int k) {     //判断后面的数够不够选的函数
	if (N1 - weizhi1 + N2 - weizhi2 >= k)
		return 1;
	else return 0;
}

//真正的递归主体
char * back_findMAX(int weizhi1, int weizhi2, int number) { 
	if (number <= 0) return 0;
	int i = weizhi1, j = weizhi2, tmp1 = -1, tmp2 = -1;
	for (; i < N1 && isok(i, j, number); i++) {
		tmp1 = MAX(tmp1, a1[i]);
	}    //找到a1数组中最大值的下标和最大值
	i = weizhi1;
	for (; j < N2 && isok(i, j, number); j++) {
		tmp2 = MAX(tmp2, a2[j]);
	}//找到a2数组中最大值的下标和最大值

	if (tmp1 > tmp2 && weizhi1 < N1) {       //选a1数组中数字的情况
		int i = weizhi1;
		while (a1[i++] != tmp1);
		weizhi1 = i - 1;

		//value = value * 10 + tmp1;
		*(p+strlen(p))=tmp1+'0';            //把值存到前面申请的内存中


		if (weizhi1 >= N1) return 0;
		back_findMAX(weizhi1 + 1, weizhi2, number - 1); // 进入下一个数字
	}
	else if (tmp1 < tmp2 && weizhi2 < N2) {   //选a2数组中数字的情况
		int j = weizhi2;
		while (a2[j++] != tmp2);
		weizhi2 = j - 1;
		//value = value * 10 + tmp2;
		*(p+strlen(p))=tmp2+'0';            //存数字

		if (weizhi2 >= N2) return 0;
		back_findMAX(weizhi1, weizhi2 + 1, number - 1);
	}
	else {                       //两个数相等的情况
		//long long int value1 = 0, value2 = 0, tmp_value = 0;
		char value1[40] = { 0 }, value2[40] = {0},  tmp_p[40] = {0};
		int i = weizhi1;
		while (a1[i++] != tmp1);
		*(p + strlen(p)) = tmp1+'0';
		strcpy(tmp_p,p);             //转存一下*p指向的字符串,后面用
		if ((i - 1) >= N1) return 0;
		back_findMAX(i, weizhi2, number - 1);
		strcpy(value1, p);     //调用结束后,将值拷贝给value1;

		int j = weizhi2;
		while (a2[j++] != tmp2);
		strcpy(p, tmp_p);
		memset((p + strlen(p)+1), 0, 40 - strlen(p)); //  这一步相当重要,*tmp_p字符串的长度
		//比*p的小,导致中间有一个'\0',后面接着还是数据,如果后续操作将这个'\0'覆盖掉,
		//将导致整个字符串读取出错
		if ((j - 1) >= N2) return 0;
		back_findMAX(weizhi1, j, number - 1);
		strcpy(value2, p);
		
		 if (strcmp(value1, value2)>=0) {  //比较两个都选情况下谁大谁小
			 strcpy(p, value1);
			 memset((p + strlen(p) + 1), 0, 40 - strlen(p));
		 }
		else
		{
			 strcpy(p, value2);
			 memset((p + strlen(p) + 1), 0, 40 - strlen(p));
		}
		return p;
	}
	return p;
}

用C语言提交上去运行时间是<1ms的,总体速度还是很快的,C++话运行时间是1.67ms。而递归对于内存的使用也是满足题目要求的。

在这里插入图片描述
在这里插入图片描述
努力学习,向大佬看齐。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值