【问题标题】:Count triplets which satisfy given condition [duplicate]计算满足给定条件的三元组[重复]
【发布时间】:2015-02-05 13:36:31
【问题描述】:

给定一个大小为 N 的数组 A,我需要计算这样的三元组 (i,j,k),这样:

Condition 1 : i < j < k
Condition 2 : A[i] > A[j] > A[k]

我知道一个O(N^3) 解决方案来做到这一点。他们可以像O(N)O(NlogN) 解决这个问题,因为N 可以达到100000

示例:N=4 和数组为[4,3,2,1],则答案为4,如{4,3,2}{4,3,1}{4,2,1}{3,2,1} 都是可能的答案

如何找到给定N 和数组A 的计数?

我的方法:

int n;
cin>>n;
vector<int> A(n);
for(int i=0;i<n;i++){
    cin>>A[i];
}
int count=0;
for(int i=0;i<n;i++){
    for(int j=i+1;j<n;j++){
        for(int k=j+1;k<n;k++){
            if(A[i]>A[j] && A[j]>A[k]){
                count++;
            }
        }
    }
}
cout<<count<<"\n";

【问题讨论】:

  • 重复项应该怎么做? [4,2,2,1] 应该返回 1 还是 2?
  • @JonathanMee 问题陈述对我来说似乎很清楚:A[0]&gt;A[1]&gt;A[3]A[0]&gt;A[2]&gt;A[3],所以答案是2。它是“计算索引集......”,而不是“计算数组中的值集......”。
  • @JonathanMee 正确答案是 2
  • 关于您的意见,您还能告诉我们什么?数字的范围是多少?
  • 如果你不能自己解决,为什么不阅读an editorial

标签: c++ algorithm dynamic-programming


【解决方案1】:

首先,对数组进行排序,维护每个元素的索引。

class Node{
    int index, val;
}

为了比较两个节点,我们首先需要比较它们的值。如果值相等,我们将比较它们的索引,如果它的索引更小,则认为一个节点更大

现在,按排序顺序处理每个节点,我们尝试将每个节点的索引添加到Fenwick tree。因此,对于每个索引i,我们在树中查询该索引的频率,该索引之前添加到树中。这是值大于当前索引值的索引数。

注意对于具有相等值的case元素,通过上面提到的排序机制,我们会先将那些索引较大的添加到树中,因此,不影响从频率值查询树。

应用类似的步骤来获得那些小于i并且索引为j &lt; i的元素。

例如:

如果我们有一个数组

{0(1) ,1(2) , 2(2) ,3(4) , 4(4) ,5(4) ,6(1)} //index(value)

After sort -> {5(4), 4(4), 3(4), 2(2), 1(2), 6(1), 0(1) }

伪代码

Node[]data;

sort(data)
Fenwick tree;
int[]less;
int[]more;
for(int i = 0; i < data.length; i++){
    less[data[i].index] = tree.query(data[i].index);
    tree.add(data[i].index, 1);
}

tree.clear();
for(int i = data.length - 1; i >= 0; i--){
    more[data[i].index] = tree.query(data.length) -tree.query(data[i].index);
    tree.add(data[i].index, 1);
}
int result = 0;
for(int i = 0; i < data.length; i++)
    result += more[i]*less[i];

时间复杂度为 O(n logn)。

工作 Java 代码(FT 是我的 Fenwick 树)

    PrintWriter out;
    Scanner in = new Scanner(System.in);

    out = new PrintWriter(System.out);
    int n = in.nextInt();
    Node[] data = new Node[n];
    for (int i = 0; i < n; i++) {
        data[i] = new Node(i + 1, in.nextInt());
    }
    FT tree = new FT(n + 2);
    Arrays.sort(data, new Comparator<Node>() {
        @Override
        public int compare(Node o1, Node o2) {
            if (o1.val != o2.val) {
                return o2.val - o1.val;
            }
            return o2.index - o1.index;
        }
    });

    int[] less = new int[n];//Store all nodes with greater index and smaller value;
    int[] greater = new int[n];//Store all nodes with smaller index and greater value
    for (int i = 0; i < n; i++) {
        greater[data[i].index - 1] = (int) tree.get(data[i].index);
        tree.update(data[i].index, 1);
    }
    tree = new FT(n + 2);
    for (int i = n - 1; i >= 0; i--) {
        less[data[i].index - 1] = (int) (tree.get(n) - tree.get(data[i].index));
        tree.update(data[i].index, 1);
    }


    long total = 0;
    for (int i = 0; i < n; i++) {
        total += less[i] * greater[i];
    }
    out.println(total);
    out.close();

【讨论】:

  • 为什么要排序两次?他们俩有什么区别吗?
  • @user3840069 你是对的,这似乎没有必要:),更新我的代码
  • 好主意..让我先编码,如果我遇到任何问题会问你,否则会接受这个答案..:)
  • @user3840069 处理这些索引要非常小心,它们是最难的部分,IMO :)
  • @user3840069 在计算较大部分时肯定有问题,所以,等我,我会尝试想出一个工作代码:)
【解决方案2】:

你可以很容易地在 O(n*n) 中做到这一点,你只需要跟踪每个元素有多少较小的数字:

vector<int> smallerNumbers(A.size());

for (int i = A.size() - 2; i >= 0; --i){
    for (int j = i + 1; j < A.size(); ++j){
        if (A[i] > A[j]){
            smallerNumbers[i]++;
            count += smallerNumbers[j];
        }
    }
}

对于 O(nklogn) 解决方案,请在此处查看我的答案:https://stackoverflow.com/a/28379003/2642059

请注意,这是一个递增序列,而您要求的是一个递减序列。

要实现这一点,您需要反转mapIndex 创建的排名。因此,只需在创建 mapIndex 之前反转 temp,只需将 partial_sort_copy 行与此行交换即可:

   partial_sort_copy(values.cbegin(), values.cend(), temp.rbegin(), temp.rend());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-09
    • 2021-02-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多