[条件]
给定的是字母{0,1,...,k},0≤k≤9。如果字中有任何两个相邻的数字,那么这个字母表中长度为n的单词紧相差不超过1。
输入是一系列行,每行包含两个整数k和n,1≤n≤
100.对于每一行输入,输出长度为n的紧密字的百分比
字母{0,1,...,k},带有5个小数位。
[输入]
4 1
2 5
3 5
8 7
[输出]
100.00000
40.74074
17.38281
0.10130
首先,我无法理解这个测验。例如,如果输入为2, 5
。我不知道为什么答案是40.74074。
在这种情况下,如果它会“紧”。中间的数字必须是1。
实施例,
00000 00001 00002
00010 00011 00012
....
所以,
这里的所有情况都是,3 5 = 243
最后一位必须是1,所以3 4 = 81将是“紧”的情况。
所以,输出必须是81/243 = 0.33333333333333333 = 33.333333%
我错过了什么吗?
还有什么好算法可以解决这个问题吗?
答案 0 :(得分:7)
(抱歉,我换了k
和n
的订单。)
如果你从紧张号码的最后一位数字开始,你会得到另一个紧张的数字,而他们的最后一位数最多相差1
。
假设您的所有数字c(n, k, l)
的长度为n
,且最后一位数为l
。然后,长度为n + 1
且最后一位为l
的紧密数字为c(n + 1, k, l) = c(n, k, l - 1) + c(n, k, l) + c(n, k, l + 1)
。
基本情况很简单:n=1
表示一个紧密的数字,即c(1, k, l) = 1
。
测试(Python):
def c(n, k, l):
if l > k or l < 0:
return 0
if n == 1:
return 1
return sum(c(n - 1, k, i) for i in range(l - 1, l + 2))
def f(n, k):
tight = sum(c(n, k, l) for l in range(k + 1))
return tight / (k + 1) ** n
示例:
>>> print(f(1,4))
1.0
>>> print(f(4, 1))
1.0
>>> print(f(5, 2))
0.4074074074074074
>>> print(f(5, 3))
0.173828125
>>> print(f(7, 8))
0.0010129691411338857
对于非常大的数字,这会变慢,因为相同的数字会反复计算。这些可以通过在程序开头添加以下两行来缓存(“memoized”)(第二行用缓存装饰以下函数c(n, k, l)
):
import functools
@functools.lru_cache()
示例:
>>> f(100,9)
1.0051226793648084e-53
答案 1 :(得分:4)
我的阅读与你的阅读略有不同:据我了解,第一个数字是字母表的大小,第二个是字母表中必须考虑的字词长度,所以:
4 1 => 100%
似乎是一个定义问题;可能的理由是,由于长度为1的单词中的数字不具有任何邻居,因此它们与它们的差异不能超过1,与字母表的大小无关,因此根据定义,长度为1的单词被认为是“紧密的”。
2 5 =&gt; 40.74074%
所以这是长度为5的单词(三位数)字母{0,1,2}。正如你所观察到的那样,有3 ^ 5个这样的单词。非紧张的词是那些(其中 x 表示“不关心”),如“xxx02”,“xxx20”,“xx02x”,“xx20x”,“x02xx”,“x20xx”, “02xxx”和“20xxx”具有2相邻的零。这8种模式中的每一种都有27种变体(每种情况下都有3种,每种情况下都有3种值),但当然会有很多重叠:“02020”最终会有3种。
所以,如果我理解正确,在没有任何捷径的情况下,解决方案必须是生成所有组合,检查每个组合中的相邻数字对(一旦你知道一个单词,你就可以提早出错)不紧张),然后计算紧或非紧的单词的数量(或者给你另一个,因为你知道集合的总大小。
答案 2 :(得分:2)
这是一些ruby代码,其输出与样本数据匹配:
#!/usr/bin/env ruby
def isTight( x )
for i in (1..x.length-1)
return false if 1 < (x[i].to_i-x[i-1].to_i).abs
end
return true
end
def getWord( x, base, n )
retval = []
1.upto(n) do
x, r = x.divmod(base)
retval.unshift r
end
retval.join
end
def percent( k, n )
nwords = (k+1) ** n
count = 0
for i in (0..nwords-1)
word = getWord( i, k+1, n )
count += 1 if isTight( word )
end
return 100.0 * count / nwords
end
STDIN.each_line do |line|
line.chomp!
puts line+' '+percent(*line.split(' ').map { |i| i.to_i }).to_s
end
这接受4行
4 1
2 5
3 5
8 7
作为输入,输出
4 1 100.0
2 5 40.74074074074074
3 5 17.3828125
8 7 0.10129691411338856
(对不起小数点后5位)
编辑:在实际操作中,您肯定希望使用WolframH的递归解决方案,其中包含完整性:
#!/usr/bin/env ruby
$cache = Hash.new
def count( k, n, last )
key = "#{k}:#{n}:#{last}"
return $cache[key] if $cache.has_key?(key)
return 0 if !(0 <= last && last <= k) # last digit must be in range
return 1 if n == 1 # single digit numbers are always tight
return $cache[key] = (-1..1).inject(0) { |sum,i| sum + count(k,n-1,last+i) }
end
def percent( k, n )
ntight = (0..k+1).inject(0) { |sum,last| sum + count(k,n,last) }
return 100.0 * ntight / (k+1)**n
end
puts percent( 1, 4 )
puts percent( 2, 5 )
puts percent( 3, 5 )
puts percent( 8, 7 )
puts percent( 9, 100 )
使用$ cache,在x86_64 Intel(R)Core(TM)i3-3240 CPU @ 3.40GHz上运行速度极快:
$ time ./tight.rb
100.0
40.74074074074074
17.3828125
0.10129691411338856
1.0051226793648083e-51
real 0m0.016s
user 0m0.010s
sys 0m0.005s
答案 3 :(得分:2)
我们的问题是找到长度为n的紧密词的数量,即a[1 .. n]
。以下是基于动态编程的解决方案。我们的想法是假设我们得到长度i - 1
的答案,我们构造一个方程来计算长度i
的答案。
让C(i, d)
是长度为i的紧密词总数,即a[1 .. i]
,最后一位数为a[i] = d
,0 <= d <= k
。观察a[i - 1] - 1 <= a[i] <= a[i - 1] - 1
(紧密词的定义),我们有以下递归关系:
For i = 1:
C(1, d) = 1
For i > 1:
C(i, d) =
C(i - 1, 0) + C(i - 1, 1) -- if d == 0
C(i - 1, k - 1) + C(i - 1, k) -- if d == k
C(i - 1, d - 1) + C(i - 1, d) + C(i - 1, d + 1) -- otherwise
那么我们所追求的只是:
N(n) = C(n, 0) + C(n, 1) + ... C(n, k)
<强> CODE:强>
这是一个nodejs程序,经过测试可以在您的示例输入中生成相同的答案(它还没有动态编程,因为我没有缓存C(i, p)
- 有很多重复计算,但应该很容易这样做)
// tight_words.js
var k = 2;
var n = 5;
function N(i) {
var n = 0;
for (d = 0; d <= k; ++d)
n += C(i, d);
return n;
}
function C(i, d) {
if (i == 1)
return 1;
if (d == 0)
return C(i - 1, 0) + C(i - 1, 1);
if (d == k)
return C(i - 1, k - 1) + C(i - 1, k);
return C(i - 1, d - 1) + C(i - 1, d) + C(i - 1, d + 1);
}
var total = Math.pow(k + 1, n);
var c = N(n);
console.log('k = ' + k + ', n = ' + n);
console.log('==> percentage = ' + c / total);
答案 4 :(得分:0)
根据WolframH的回答,我尝试了 C ++ 中的示例输入问题,但似乎有效。我还尝试了 phython 解决方案,它可以很好地处理示例输入。有趣的是,当我将输入增加到更大的数字(即3和18)时,我在 C ++ 和 phython 中的两个解决方案都会挂起不确定的时间。
出了什么问题?
非常巧合的是,昨天晚上我碰巧经历了我的Dynamic Programming笔记,并阅读了Weighted Independent Set Problem。啊哈!我们做的工作远远超出了我们的预期!在:
#include <math.h>
#include <iomanip>
#include <iostream>
using namespace std;
void get_tight_number(int base_max, int length)
{
double result = 0;
int _tight_numbers = 0;
double total = pow(base_max + 1, length);
for (int i = 0; i <= base_max; ++i)
{
_tight_numbers += get_tight_number_help(base_max, length, i);
}
result = 100 * _tight_numbers / total;
cout << fixed << setprecision(5) << result << "\n";
}
int get_tight_number_help(int base_max, int length, int endwith)
{
cout << "Length: " << length << "endwith: " << endwith << endl;
if (length < 1 || endwith < 0 || endwith > base_max)
return 0;
if (length == 1)
{
return 1;
} else
{
return get_tight_number_help(base_max, length - 1, endwith)
+ get_tight_number_help(base_max, length - 1, endwith + 1)
+ get_tight_number_help(base_max, length - 1, endwith - 1);
}
}
int main()
{
get_tight_number(8, 7);
return 0;
}
大量prints
且结果正确0.10130
。如果我grep "endwith:" | wc -l
我得到7719
,这意味着,对于此输入,辅助函数被调用超过7000次!为了了解在其他输入上调用了多少次,我得到了:
Input #
8, 8 22254
8, 6 2682
8, 5 933
不是很好......我们做了太多的重新计算。相反,我将参考数组的bottom up
解决方案放在一起:
int** tight_number_bottom_up(int base_max, int length)
{
int** result = new int*[base_max + 1];
for (int i = 0; i < base_max + 1; ++i)
{
result[i] = new int[length];
}
//Ends with i, i.e., looping over them
for (int j = 0; j < length + 1; ++j)
{
for (int i = 0; i < base_max + 1; ++i)
{
if (j == 0)
{
result[i][j] = 0;
} else if (j == 1)
{
result[i][j] = 1;
} else
{
int bigger = i == base_max ? 0 : result[i + 1][j - 1];
cout << "bigger: " << bigger << endl;
int smaller = i == 0 ? 0 : result[i - 1][j - 1];
cout << "smaller: " << smaller << endl;
result[i][j] = result[i][j - 1] + bigger + smaller;
}
}
}
return result;
}
我确信形成自下而上表的迭代次数最多为(base_max + 1) * (length + 1)
,很高兴我写完了并且很高兴它能给出正确的结果。
后续问题(如果您还在我身边)
对于double
甚至9, 100
等输入来说, 9, 50
似乎还不够,我能做些什么才能让“长”的人加倍?