C++开发工程师 京东 2018秋招编程题 (代码后续更新。。。)
1、神奇数
将一个数n的各数位分成两部分,两部分之和相等,则称这个数为神奇数。比如242分成[2,2]、[4]。输入一个范围[l, r],输出这个范围内神奇数的个数。
示例:
输入:
1,50
输出:
4
解析:
判断一个数是否是神奇数,首先应该获取其各个数位{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[n][newSum]即表示是否可以找到一个子集,其元素和为sum/2。
C++代码实现
#include <iostream>
#include <vector>
using namespace std;
int l=0,r=0;
//动态规划
bool canPartition(vector<int>&dicts,int n,int sum)
{
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][j] || dp[i-1][j-dicts[i-1]];
}
}
return dp[n][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);
}
int sqs()
{
if(r<11)
return 0;
if(r==11)
return 1;
int cnt = 1;
for(int i=12; i<=r; i++){
if(isSqs(i))
cnt++;
}
return cnt;
}
int main()
{
cin>>l>>r;
cout<<sqs();
return 0;
}
2、疯狂的数列
有一个无限数列,{1,2,2,3,3,3,4,4,4,4,…},数字n在数列中出现n次,且是连续的。输入一个整数n,输出第n项数字。n的范围为[1,10^18]。
示例:
输入:
169
输出:
18
解析:
用dp[i]表示数字i在无限数列的索引,则有:
dp[1] = 1
dp[2] = 3
dp[3] = 6
dp[4] = 10
dp[5] = 15
…
dp[n] = n*(n+1)/2
因此判断第n项的数字,应该找到dp[i]<= n <=dp[i+1],
这里的i 或者 i+1 即为结果。
C++代码实现
下面这个代码会超时。
int getIndex(long long& n)
{
if(n==1)
return 1;
long long sum = 0,last = 1;
for(int i=sqrt(n); i<n && sum<=n; i++){
sum = ((i+1)*i)>>1;
if(sum==n)
return i;
else if(sum < n)
last = i;
}
return last+1;
}
int main()
{
long long n;
cin>>n;
cout<<getIndex(n);
return 0;
}