我解决了这个问题,但我在网上判断时
程序的输出是正确的,但我认为可以改进方式以提高效率!
问题:
给定n
整数,计算我们可以选择两个元素的方式
他们的绝对差异小于32
。
以更正式的方式,计算(i, j) (1 ≤ i < j ≤ n)
对的数量
|V[i] - V[j]| < 32
。 |X|
是X
的绝对值。
输入
第一行输入包含一个整数T
,即测试用例数(1 ≤ T ≤ 128)
。
每个测试用例都以整数n (1 ≤ n ≤ 10,000)
开头。
下一行包含n
个整数(1 ≤ V[i] ≤ 10,000)
。
输出
对于每个测试用例,在一行上打印对数。
我在c ++中的代码:
int main() {
int T,n,i,j,k,count;
int a[10000];
cin>>T;
for(k=0;k<T;k++)
{ count=0;
cin>>n;
for(i=0;i<n;i++)
{
cin>>a[i];
}
for(i=0;i<n;i++)
{
for(j=i;j<n;j++)
{
if(i!=j)
{
if(abs(a[i]-a[j])<32)
count++;
}
}
}
cout<<count<<endl;
}
return 0;
}
我需要帮助如何在更高效的算法中解决它?
答案 0 :(得分:6)
尽管我之前(愚蠢)回答,但根本不需要对数据进行排序。相反,你应该计算数字的频率。
然后,您需要做的就是跟踪要配对的可行数的数量,同时迭代可能的值。抱歉没有c ++,但java也应该是可读的:
int solve (int[] numbers) {
int[] frequencies = new int[10001];
for (int i : numbers) frequencies[i]++;
int solution = 0;
int inRange = 0;
for (int i = 0; i < frequencies.length; i++) {
if (i > 32) inRange -= frequencies[i - 32];
solution += frequencies[i] * inRange;
solution += frequencies[i] * (frequencies[i] - 1) / 2;
inRange += frequencies[i];
}
return solution;
}
答案 1 :(得分:3)
ex -s +':$s@$@\ralias foo=bar@' -cwq /etc/bash.bashrc
答案 2 :(得分:2)
您可以对数字进行排序,然后使用滑动窗口。从最小的数字开始,用std::deque
填充数字,只要它们不大于最小数字+31。然后在每个数字的外部循环中,更新滑动窗口并添加新的大小滑动窗口到柜台。滑动窗口的更新可以在内部循环中执行,首先pop_front
每个小于外部循环的当前数字的数字,然后push_back
每个不大于当前数字的数字外环+ 31。
答案 3 :(得分:1)
一个更快的解决方案是首先对数组进行排序,然后遍历排序的数组,并且每个元素只访问右边的元素,直到差异超过31。
排序可以通过计数排序(因为你有1≤V[i]≤10,000)。因此,您可以获得分拣部分的线性时间。虽然可能没有必要(也许快速排序足以获得所有积分)。
另外,你可以为内循环做一个技巧(“走到当前元素的右边”部分)。请记住,如果S [i + k] -S [i] <32,则S [i + k] -S [i + 1] <32,其中S是V的排序版本。整个算法变成线性的。
答案 4 :(得分:1)
这可以对数据进行恒定的传递次数,实际上可以在不受&#34; interval&#34;的值的影响的情况下完成。 (在你的情况下,32)。
这是通过在a[i] = a[i-1] + number_of_times_i_appears_in_the_data
- 非正式地填充数组来完成的,a[i]
包含小于/等于i
的元素总数。
代码(针对单个测试用例):
static int UPPER_LIMIT = 10001;
static int K = 32;
int frequencies[UPPER_LIMIT] = {0}; // O(U)
int n;
std::cin >> n;
for (int i = 0; i < n; i++) { // O(n)
int x;
std::cin >> x;
frequencies[x] += 1;
}
for (int i = 1; i < UPPER_LIMIT; i++) { // O(U)
frequencies[i] += frequencies[i-1];
}
int count = 0;
for (int i = 1; i < UPPER_LIMIT; i++) { // O(U)
int low_idx = std::max(i-32, 0);
int number_of_elements_with_value_i = frequencies[i] - frequencies[i-1];
if (number_of_elements_with_value_i == 0) continue;
int number_of_elements_with_value_K_close_to_i =
(frequencies[i-1] - frequencies[low_idx]);
std::cout << "i: " << i << " number_of_elements_with_value_i: " << number_of_elements_with_value_i << " number_of_elements_with_value_K_close_to_i: " << number_of_elements_with_value_K_close_to_i << std::endl;
count += number_of_elements_with_value_i * number_of_elements_with_value_K_close_to_i;
// Finally, add "duplicates" of i, this is basically sum of arithmetic
// progression with d=1, a0=0, n=number_of_elements_with_value_i
count += number_of_elements_with_value_i * (number_of_elements_with_value_i-1) /2;
}
std::cout << count;
在IDEone上完整的示例。
答案 5 :(得分:1)
这个解决方案可以被认为是O(N)来处理N个输入数字并且在时间上恒定来处理输入:
#include <iostream>
using namespace std;
void solve()
{
int a[10001] = {0}, N, n, X32 = 0, ret = 0;
cin >> N;
for (int i=0; i<N; ++i)
{
cin >> n;
a[n]++;
}
for (int i=0; i<10001; ++i)
{
if (i >= 32)
X32 -= a[i-32];
if (a[i])
{
ret += a[i] * X32;
ret += a[i] * (a[i]-1)/2;
X32 += a[i];
}
}
cout << ret << endl;
}
int main()
{
int T;
cin >> T;
for (int i=0 ; i<T ; i++)
solve();
}
解决方案说明:a[i]
表示输入系列中i
的次数。
然后你遍历整个数组,X32跟踪范围为i
的元素数量。真正唯一棘手的部分是在多次重复某些i时正确计算:a[i] * (a[i]-1)/2
。就是这样。
答案 6 :(得分:1)
你可以排序,然后在范围熄灭时使用break to end循环。
ngOnChanges
或者,您可以使用哈希数组作为范围并标记每个元素的出现,然后循环并检查每个元素,即是否存在x = 32-y。
答案 7 :(得分:1)
这里的一个好方法是将数字分成不同的桶:
constexpr int limit = 10000;
constexpr int diff = 32;
constexpr int bucket_num = (limit/diff)+1;
std::array<std::vector<int>,bucket_num> buckets;
cin>>n;
int number;
for(i=0;i<n;i++)
{
cin >> number;
buckets[number/diff].push_back(number%diff);
}
显然,同一个桶中的数字足够接近以满足要求,因此我们可以统计所有对:
int result = std::accumulate(buckets.begin(), buckets.end(), 0,
[](int s, vector<int>& v){ return s + (v.size()*(v.size()-1))/2; });
非相邻存储桶中的数字不能形成任何可接受的对,因此我们可以忽略它们。
这留下了最后一个角落 - 相邻的水桶 - 可以通过多种方式解决:
for(int i=0;i<bucket_num-1;i++)
if(buckets[i].size() && buckets[i+1].size())
result += adjacent_buckets(buckets[i], buckets[i+1]);
我个人喜欢一桶规模的“发生频率”方法,但可能有更好的选择:
int adjacent_buckets(const vector<int>& bucket1, const vector<int>& bucket2)
{
std::array<int,diff> pairs{};
for(int number : bucket1)
{
for(int i=0;i<number;i++)
pairs[i]++;
}
return std::accumulate(bucket2.begin(), bucket2.end(), 0,
[&pairs](int s, int n){ return s + pairs[n]; });
}
此函数首先构建一个“来自较低桶的数字,其数量足够接近i
”的数组,然后将该数组中与上部存储桶编号对应的值相加。
一般来说,这种方法具有O(N)复杂度,在最好的情况下,它只需要一次传递,总体上应该足够快。
答案 8 :(得分:0)
您应该首先对输入进行排序。
然后,如果你的内环检测到距离增长到32以上,你可以从中断。
答案 9 :(得分:0)
感谢大家努力和时间来解决这个问题。
我很感激所有尝试解决它。
在对在线评判的答案进行测试后,我发现正确且最有效的解决方案算法是Stef's Answer和AbdullahAhmedAbdelmonem's answer也是pavel解决方案是正确的,但它与Stef解决方案在不同语言中完全相同C ++。
Stef的代码在代码强制在线判断和接受时,执行时间 358 ms 。
AbdullahAhmedAbdelmonem的代码在代码强制在线判断并接受了 421 ms 时间执行。
如果他们对那里的算法进行详细的解释,那么赏金将是其中之一。
您可以尝试解决方案,并在选择问题E. Time Limit Exceeded?
后将其提交给代码强制在线判断此链接我也找到了一个很好的算法解决方案,使用频率数组及其复杂度O(n)更容易理解。
在此算法中,您只需要为每个插入的元素采用特定的范围:
begin = element - 32
end = element + 32
然后计算频率数组中每个插入元素在此范围内的对数:
int main() {
int T,n,i,j,k,b,e,count;
int v[10000];
int freq[10001];
cin>>T;
for(k=0;k<T;k++)
{
count=0;
cin>>n;
for(i=1;i<=10000;i++)
{
freq[i]=0;
}
for(i=0;i<n;i++)
{
cin>>v[i];
}
for(i=0;i<n;i++)
{
count=count+freq[v[i]];
b=v[i]-31;
e=v[i]+31;
if(b<=0)
b=1;
if(e>10000)
e=10000;
for(j=b;j<=e;j++)
{
freq[j]++;
}
}
cout<<count<<endl;
}
return 0;
}
最后我认为解决这类问题的最佳方法是使用频率数组并计算特定范围内的对数,因为它的时间复杂度为O(n)。