小米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的长度),方便后面数据读取使用。
- 首先判断是否N1+N2==k,如果是,直接从头开始比较大小选数,这种情况很简单
- 如果不是(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;
- 写好边界条件后,从a,b数组开始读取数据。比较MAX(a[i],b[j]),谁大选谁;
- 如果出现a[i]=a[j]的情况,那就两个都选,选好后保存两种情况下的函数返回值,然后做一个比较。将最大值赋给返回值。
- 选好当前值后,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。而递归对于内存的使用也是满足题目要求的。
努力学习,向大佬看齐。