0

问题本质上是我发现 union-find 方法的递归版本比迭代版本快。

// the union find method - recursive
int find(int k) {
    if(f[k] != k) f[k] = find(f[k]);
    return f[k];
}

// the union find method - iterative
int find(int k){
    while(f[k] != k) k = f[k];
    return f[k];
}

问题的背景在这里:好或坏的平衡

问题说我们有一个平衡,但我们不知道它是好是坏。我们有两种重量不同的物品,同一种物品重量相同。我们将所有项目索引到 1,2,3..n。我们随机挑选其中两个并称重。每个称重结果以 x,u,v 的形式表示,其中 x 是位指示符,0 表示平衡,1 表示不平衡,而 u 和 v 是两个项目的索引。

如果天平不好,就会出现矛盾,比如我们有这样的称量结果:

0 1 2
1 1 2

item 1 和 item 2 在两个措施中的关系不同,所以平衡不好。我需要编写一个程序来告诉最早可以确定余额是坏还是好的度量。

基本上,这是经典并集问题的变体。我希望迭代联合查找方法可以带来更好的性能,但是当递归版本被接受时,我得到了 Time Limit Exceeded。我想问这是为什么???

这是我算法的完整版本。

#include <iostream>
#include <vector>

using namespace std;

#define N 10009

int n, m;
int x,u,v;
vector<int> f(2*N+1,0);

// iterative
int find(int k) {
    if(k!=f[k]) f[k] = find(f[k]);
    return f[k];
}

// recursive
// int find(int k) {
//     while(k!=f[k]) k=f[k];
//     return f[k];
// }

int main(){
    ios_base::sync_with_stdio(false); 
    cin.tie(NULL);
    int T; // T is number of test cases
    cin >> T;
    while(T--){
        // n is number of items, m is number of measurements
        cin >> n >> m;
        f.resize(2*n+1);
        for(int i = 0 ; i < 2*n+1; ++i){
            f[i] =i;
        }

        bool flag = false;
        int ans = 0;
        int r1, r2;
        for(int i = 1 ; i <= m ; ++i){
            // x is weighing result, u and v are item id.
            cin >> x >> u >> v;
            if(flag) continue;
            if(x == 0) {
                // two items are balance
                if(find(u) == find(v+n) || find(v) == find(u+n)){
                    flag = true;
                    ans = i;
                    continue;
                }
                r1 = find(u); r2 = find(v);f[r2] = r1;
                r1 = find(u+n); r2= find(v+n); f[r2] = r1;
            } else {
                // two items are imbalance
                if(find(u) == find(v)){
                    flag = true;
                    ans = i;
                    continue;
                }
                r1 = find(u); r2= find(v+n);f[r2]=r1;
                r1 = find(v); r2 = find(u+n);f[r2]=r1;
            }               
        }
        if(flag){
            cout << "sad" << endl;
            cout << ans << endl;
        } else
            cout << "great" << endl;
    }


    return 0;
}

一个示例测试用例是

2
4 5
0 1 3
1 2 4
0 1 4
0 3 4
0 1 2
2 2
0 1 2
1 1 2
4

1 回答 1

2

递归和迭代版本不做同样的事情。递归版本将f[k]根据结果进行更新,因此在随后的具有相同k值的调用中,最多可以通过一次递归调用找到答案。如果已经找到中间值之一,即使在初始调用中也可以观察到效果。

迭代版本不会更新f数组,因此后续调用必须执行完整循环,直到找到答案。

优化器也有可能内联一些递归,因此它不像最初出现的那样递归。

于 2017-11-21T05:18:00.470 回答