您的位置:首页 > 编程语言

京东2018秋招编程题

2017-09-10 13:51 375 查看

1.神奇数

将一个数n的各数位分成两部分,两部分之和相等,则称这个数为神奇数。比如242分成[2,2]、[4]。输入一个范围[l,

r],输出这个范围内神奇数的个数。

第一种解法,使用回溯法

采用回溯法进行递归搜索

#include <iostream>
#include <vector>
#include <algorithm>
#include<windows.h>
using namespace std;

long min_num=0,max_num=0;
long l;

bool opt(int a,int b){
return a<b;
}

bool recall(vector<int> vec,int l,int pos,int sum){
for(int i=pos;i<vec.size();i++){
l+=vec[i];
if(l==sum){
return true;
}
if( recall(vec,l,i+1,sum) ){
return true;
}
if(l>sum){
l-=vec[i];
return false;
}
l-=vec[i];
}
return false;
}

bool get_digits_recall(long i){
vector<int> vec;
long m=i;
int sum=0;
int digit;
while(m>0){
if(m<10) {
vec.push_back(m);
sum+=m;
break;
}
digit = m%10;
sum+=digit;
if(digit>0)
vec.push_back(digit);
m/=10;
}

if(sum&1) return false;
l=0;
sort(vec.begin(),vec.end(),opt);

return recall(vec,l,0,sum>>1);
}

int strange_number(){
if(max_num<11) return 0;
int cnt=0;
for(long i=min_num;i<max_num;i++){
if(get_digits_recall(i)){
cnt++;
}
}
return cnt;
}

int main(int argc, char const *argv[])
{
cin>>min_num>>max_num;

DWORD start_time=GetTickCount();

cout<<strange_number();

DWORD end_time=GetTickCount();
cout<<"\nThe run time is:"<<(end_time-start_time)<<"ms!"<<endl;//输出运行时间

return 0;
}


第二种解法,使用动态规划

判断一个数是否是神奇数,首先应该获取其各个数位{a1, a2, a3, a4,

…},按照神奇数的定义,需要将数位分成两部分,且两部分元素和相等,因此所有数位的和sum{a1, a2, a3, a4,

…}必为偶数。如果不是偶数,则必然不是神奇数。 因此,这个问题就演变成,在{a1, a2, a3, a4,

…}中寻找一个子集,使得sum{子集} = sum/2。 显然,这是一个0-1背包问题。可以用回溯法、动态规划。

这里使用动态规划,用dp[i][j]表示子数组{a1, a2, a3, a4,…, ai}的元素和是否等于j。 初始dp[i][0] =

true (所有元素都不选,和为j) 如果不选择第i个元素,则dp[i][j] = dp[i-1][j]

如果选择第i个元素,则dp[i][j] = dp[i-1][j-num[i]] 因此,dp[i][j] = dp[i-1][j] ||

dp[i-1][j-num[i]]。 dp
[newSum]即表示是否可以找到一个子集,其元素和为sum/2。



#include <iostream>
#include <vector>
#include<windows.h>
using namespace std;
int l=0,r=0;

//动态规划
bool canPartition(vector<int>&dicts,int n,int sum)  //n代表vector的非零位数
{
vector<vector<bool>> dp(n+1,vector<bool>(sum+1,false));
for(int i=0; i<=n; i++)
dp[i][0] = true;
for(int j=1; j<=sum; j++){
for(int i=1; i<=n; i++){
dp[i][j] = dp[i-1][j]; //不选第i个元素
if(j>=dicts[i-1])      //选第i个元素
dp[i][j] = dp[i-1][j] || dp[i-1][j-dicts[i-1]];
}
}

for(int i=0;i<n;i++)
cout<<dicts[i]<<" ";
cout<<endl;
for(int j=0; j<=sum; j++){
for(int i=0; i<=n; i++){
cout<<"dp["<<i<<"]["<<j<<"]="<<dp[i][j]<<" ";
}
cout<<endl;
}

return dp
[sum];
}

bool isSqs(int num)
{
vector<int> dicts(10,0);
int k=0,sum=0;
while(num!=0){
dicts[k++] = num%10;
num /= 10;
sum += dicts[k-1];
}
if(sum & 1)
return false;
return canPartition(dicts,k,sum>>1);    //sum>>1表示除以2
}

int sqs()
{
if(r<11)
return 0;
int cnt = 0;
for(int i=l; i<=r; i++){    //依次对每一个数进行测试
if(isSqs(i))
cnt++;
}
return cnt;
}

int main()
{
cin>>l>>r;

DWORD start_time=GetTickCount();

cout<<sqs();

DWORD end_time=GetTickCount();
cout<<"The run time is:"<<(end_time-start_time)<<"ms!"<<endl;//输出运行时间
return 0;
}


2.疯狂的序列

有一个无限数列,{1,2,2,3,3,3,4,4,4,4,…},数字n在数列中出现n次,且是连续的。输入一个整数x,输出序列的第x项的数字。x的范围为[1,10^18]。

可以看出是数字n的最后一个n的位置是序列中的第n*(n+1)/2项.

使用for循环,但是会超时

根据 (n+1)*n/2 = x , 得到n^2+n-2x=0 , 解得n=sqrt(1+8x)-1

#include <iostream>
#include <math.h>

using namespace std;

int main(){
long i;
cin>>i;
double x = (sqrt((double)(1+8*i))-1)/2;
cout<<ceil(x)<<endl;//向上取整
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: