码迷,mamicode.com
首页 > 其他好文 > 详细

对一道编程题的后续思考

时间:2015-04-07 17:25:12      阅读:166      评论:0      收藏:0      [点我收藏+]

标签:

  原题来自《一道整数求值作业》

  题目描述:给定一个整数 X,找到组成的数字和 X 完全相同的,且大于 X 的最小的那个数;若不存在这样的数,输出 0

  一开始把它想复杂了,后来想想只需将该数组成的数列从后往前枚举,然后判断当前位置往后的数列是否是降序即可,时间复杂度O(n),详细思路跟上文一致可以参考原文。

var a = 21544221; // 要求的数字
var s = a.toString();
var len = s.length;
for(var i = len - 2; i >= 0; i--) {
  if(s[i] >= s[i + 1]) continue;
  for(var j = len - 1; j > i; j--) {
    if(s[j] > s[i]) {
      var index = j;
      break;
    }
  }
  break;
}
var arr = s.split(‘‘);
if(i !== -1) {
  arr[i] = [arr[index], arr[index] = arr[i]][0];
  var ans = arr.slice(0, i + 1).concat(arr.slice(i + 1).reverse()).join(‘‘);
}
console.log(~~ans);

  之后我陷入了思考:如果不是要求比原数大的最小的数,而是第k大的呢

  为了不至于复杂度太高以致cpu假死,我把所求数暂定在int32范围内,写了个计算第k大数的暴力程序用来验证,复杂度O(n!)

function dfs(a, index) {
  if(index === len) {
    if(hash_ans[a] || a < num) 
      return;
    ans.push(a);
    hash_ans[a] = true;
    return;
  }

  for(var i = 0; i < len; i++) {
    if(hash_pos[i]) continue;
    hash_pos[i] = true;
    dfs(~~a * 10 + ~~arr[i], index + 1);
    hash_pos[i] = false;
  }
}

var num = 131443134; // 要求的数字
var k = 818; // 比num大的第k大数
var arr = num.toString().split(‘‘);
var len = num.toString().length;
var ans = []; // 存放比num大的数
var hash_pos = {};
var hash_ans = {};
dfs(0, 0);
ans.sort();
ans.length <= k ? console.log(0) : console.log(ans[k]);

  然后开始思索第k大解,发现后向扫描仍然可解,但是复杂度应该不低,在没有想到其他解法前先一试。后向扫描的依据是所组成的新的数从前往后跟原来的数比较,在经过一系列相等的比较后,肯定会出现一个大于原数的相同位置。于是我们根据此枚举该位置,枚举到的位置的数用位置后面的比它大的数代替(以保证新的数比原数大),确定该位置的数之后,后面的位置进行全排列即是确定该位置后的所有解(注意全排列后需除相同数字的全排列),同时我们需要维护一个变量保存当前枚举位置后的总的解个数,如果个数还没达到k,则继续在该位置枚举更大的数,或者枚举位置继续前移;当发现总个数大于k时,该位置就确定是这个数了,接着进行第二轮枚举,枚举过程相似。在枚举的过程中反复地出现某个临界点(大于小于k),而出现临界点的同时该位置的数确定。

var A = [];
A[0] = 1;
for(var i = 1; i <= 10; i++)
  A[i] = A[i - 1] * i;
var a = 131443134;
var k = 818;

var total = 0;
var arr = a.toString().split(‘‘); // 字符串数组
var len = arr.length;
var hash = [];  // 记录后向扫描中0~9的数量
for(var i = 0; i <= 9; i++)
  hash[i] = 0;
hash[a % 10] = 1;
var maxn = a % 10;  // 维护后向扫描中最大的数字

for(var i = len - 2; i >= 0; i--) {
  hash[~~arr[i]]++;
  if(maxn > ~~arr[i]) { // 可以交换
    for(var j = ~~arr[i] + 1; j <= 9; j++) {  // 该位置可以换的数字
      if(!hash[j]) continue;
      var add = A[len - i - 1];  // 全排列
      for(var l = 0; l <= 9; l++) {
        l === j ? add /= A[hash[l] - 1] : add /= A[hash[l]]; // 除去相同数字的全排列
      }
      
      if(total + add >= k) {  // 就是这个交换
        hash[j]--;
        // 第i位变成了j 
        var ans = a.toString().substring(0, i) + j + getAns(i);
      } else {
        total += add;
      }

      if(ans) break;
    }
  }
  maxn = Math.max(maxn, ~~arr[i]);
  if(ans) break;
}

function getAns(a) { // 第a位变成了b
  var res = ‘‘;
  for(var i = a + 1; i < len; i++) {
    for(var j = 0; j <= 9; j++) { // 从小到大枚举i位置的数字
      if(!hash[j]) continue;
      var add = A[len - i - 1];  // 全排列
      for(var l = 0; l <= 9; l++) 
        l === j ? add /= A[hash[l] - 1] : add /= A[hash[l]]; // 除去相同数字的全排列
      if(total + add >= k) {  // 就是这个交换
        hash[j]--;
        res += j;
        break;  // i位置确定是j
      } else {
        total += add;
      }
    }
  }
  return res;
}

ans ? console.log(ans) : console.log(0);

  解是有了,好像没涉及什么算法,倒是觉得像个复杂的模拟,看了下时间复杂度,最坏情况下应该会达到O(n*10*10),似乎效率还可以,但是我一开始想到这个问题的时候,似乎觉得会是一道动态规划,或者是按位dp?无奈对于算法了解不多,只能日后接触了再进行思考,也希望有更高效解法的博友留言交流

对一道编程题的后续思考

标签:

原文地址:http://www.cnblogs.com/zichi/p/4396548.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!