构造最大数
问题描述
给出两个长度分别是 m 和 n 的数组来表示两个大整数,数组的每个元素都是数字 0-9。从这两个数组当中选出 k 个数字来构造一个最大数,其中 k 满足 k <= m + n。选出来的数字在构造的最大数里面的位置必须和在原数组内的相对位置一致。输出这有k个数字构成的最大数。
注:尽可能的降低算法的时间复杂度和空间复杂度。
输入
有多组测试数据。
每组数据有3行,第1行有整数m、n、k,0<m,n<100,第2行和第3行分别是长度为m、n的字符串表示的数。
输出
对每组数据,输出选出的最大数。
输入样例
4 6 5
3465
912583
输出样例
98653
思路:
直接挑选似乎有点难,我们不妨将该问题拆分成两个子问题。
一:从数字a中选出i个数字的最大数tmp1,从数字b中选出k-i个数字的最大数tmp2, 注意选出的最大数在原数组中相对位置不变;
二,将两个数字tmp1,tmp2重新不改变原数组中相对位置情况下组成一个新的最大数字。
从以上两个子问题对 i 进行枚举我们便有了一个O(n ^ 3)的算法。
子问题一:从数字a中不改变相对顺序挑选中i个数字的最大数。
首先我们要挑出i个数字,那么显然的我们就要在原字符串中删去n-i个数字。同时很重要的一点是不能改变相对次序,那么我们部分先把数组a的首位数字放入,将下一位数字和之前放入的数字进行比较,小于则继续放入末尾,大于那就将之前放入的数字删除,这个时候我们就从原数组中删除了1位数字,一共要删除n - i个数字。当然如果数字a是按照递减顺序排列,那么可能不会删除数字,所以最后我们要将获得的数字长度置为i。代码如下:
void Findnum(const char *a,char *res,int n,int k){
int dlen = n - k; //delete len
int len = 0;
for(int i = 0; a[i]; ++i){
while(dlen && len && res[len-1] < a[i]){ //比较下一位和已经加入数字字符串中最后一位数字的大小
--len, --dlen;
}
res[len++] = a[i];
}
res[len = k] = '\0'; //resize len = k;
}
子问题二:将两个数字tmp1,tmp2重新不改变原数组中相对位置情况下组成一个新的最大数字。
其实是类似一个归并排序的思想。首位大的组成新的数字的首位,然后移动到下一位,唯一要注意的是首位相同的时候,不能随便选取一个数字进行合并,要旋转数字字符串大的先进行合并。代码如下:
void Merge(char *a,char *b,char *res){
int len = 0;
while(*a !='\0' && *b != '\0'){
if(strcmp(a,b)>=0){ //strcmp(a,b) 不是 *a > * b ; 举例:abc,acc
res[len++] = *a;
++a;
}
else{
res[len++] = *b;
++b;
}
}
while(*a != '\0'){
res[len++] = *a;
++a;
}
while(*b != '\0'){
res[len++] = *b;
++b;
}
res[len] = '\0';//'\0'不能漏
}
至此,就只剩下枚举了,完整代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100 + 5;
void Findnum(const char *a,char *res,int n,int k){
int dlen = n - k;
int len = 0;
for(int i = 0; a[i]; ++i){
while(dlen && len && res[len-1] < a[i]){
--len, --dlen;
}
res[len++] = a[i];
}
res[len = k] = '\0';
}
void Merge(char *a,char *b,char *res){
int len = 0;
while(*a !='\0' && *b != '\0'){
if(strcmp(a,b)>=0){
res[len++] = *a;
++a;
}
else{
res[len++] = *b;
++b;
}
}
while(*a != '\0'){
res[len++] = *a;
++a;
}
while(*b != '\0'){
res[len++] = *b;
++b;
}
res[len] = '\0';
}
void solve(const char *a,int n,const char *b,int m,int k){
char ans[maxn<<1]; //两倍空间
ans[0] = '\0'; //赋值为'\0'很重要,局部变量未知
char res[maxn<<1],tmp1[maxn],tmp2[maxn]; //注意字符指针都需要开辟空间,不开空间会炸
for(int i = 0; i <= k; ++i){
if(i > n || k - i > m) continue ;
Findnum(a,tmp1,n,i); //a中找长度为i的最大数字tmp1,
//puts(tmp1);
Findnum(b,tmp2,m,k - i);//b中找长度为k - i的最大数字tmp2
//puts(tmp2);
Merge(tmp1,tmp2,res); //合并tmp1,tmp2到res,
//puts(res);
if(strcmp(res,ans) > 0) strcpy(ans,res); //记录最大值ans
}
puts(ans);
}
int main(){
int n,m,k;
char a[maxn],b[maxn];
while(~scanf("%d%d%d",&n,&m,&k)){
scanf("%s%s",a,b);
solve(a,n,b,m,k);
}
return 0;
}