在数字圆桌中找到最长的后续子序列

时间:2014-09-21 19:50:45

标签: c++ algorithm lis

我最近在研究以下问题。 http://www.codechef.com/problems/D2

厨师正计划为DirectiPlex就职典礼举办自助餐,并邀请所有人参加。在他们的路上,每位客人拿起一张包含随机数的纸(这个数字可能会重复)。然后客人和他们的朋友坐在圆桌旁。 厨师现在决定他想玩游戏。他要求你从桌子上挑选一个随机的人,让他们大声读出他们的号码。然后,围绕桌子顺时针移动,每个人都会读出他们的号码。目标是找到形成不断增加的子序列的数字集。拥有这些数字的所有人都有资格参加幸运抽奖!其中一位软件开发人员对这一前景感到非常兴奋,并希望最大限度地增加有资格参加抽奖的人数。因此,他决定编写一个程序,决定谁应该首先阅读他们的号码,以便最大化有资格参加抽奖的人数。你能打败他吗?

输入

第一行包含t,测试用例数(约15)。然后是t个测试用例。每个测试用例包含两行:

  1. 第一行包含一个号码N,即邀请参加聚会的嘉宾人数。
  2. 第二行包含由空格分隔的N个数字a1, a2, ..., an,这些是以顺时针顺序写在纸张上的数字。
  3. 输出

    对于每个测试用例,请打印包含单个号码的行,该号码是有资格参加抽奖的最大客人人数。

    约束

    1 ≤ N ≤ 10000 您可以假设纸张上的每个号码; ai是随机生成的,即来自区间[0,U]的任何数字的概率相等,其中U是某个上限(1 ≤ U ≤ 106)。

    示例

    输入:

    3    
    2    
    0 0    
    3    
    3 2 1    
    6    
    4 8 6 1 5 2
    

    输出:

    1    
    2    
    4
    

    在检查解决方案时,我找到了这段代码:

    #include <iostream>
    #include <vector>
    #include <stdlib.h>
    #include <algorithm>
    
    #define LIMIT 37
    
    using namespace std;
    
    struct node {
        int val;
        int index;
    };
    
    int N;
    
    int binary(int number, vector<int>& ans) {
        int start = 0;
        int n = ans.size();
        int end = n - 1;
        int mid;
        if (start == end)
            return 0;
        while (start != end) {
            mid = (start + end) / 2;
            if (ans[mid] == number)
                break;
            if (ans[mid] > number)
                end = mid;
            else
                start = mid + 1;
        }
        mid = (start + end) / 2;
        return mid;
    }
    
    void display(vector<int>& list) {
        cout << endl;
        for (int i = 0; i < list.size(); i++)
            cout << list[i] << " ";
        cout << endl;
    }
    
    int maxsubsequence(vector<int>& list) {
        vector<int> ans;
        int N = list.size();
        ans.push_back(list[0]);
        int i;
        // display(list);
        for (i = 1; i < N; i++) {
            int index = binary(list[i], ans);
            /*if(index+1<ans.size())
                continue;*/
            if (list[i] < ans[index])
                ans[index] = list[i];
            if (list[i] > ans[index])
                ans.push_back(list[i]);
            // display(ans);
        }
        return ans.size();
    }
    
    int compute(int index, int* g) {
        vector<int> list;
        list.push_back(g[index]);
        int itr = (index + 1) % N;
        while (itr != index) {
            list.push_back(g[itr]);
            itr = (itr + 1) % N;
        }
        return maxsubsequence(list);
    }
    
    int solve(int* g, vector<node> list) {
        int i;
        int ret = 1;
        for (i = 0; i < min(LIMIT, (int)list.size()); i++) {
            // cout<<list[i].index<<endl;
            ret = max(ret, compute(list[i].index, g));
        }
        return ret;
    }
    
    bool cmp(const node& o1, const node& o2)
    { return (o1.val < o2.val); }
    
    int g[10001];
    
    int main() {
        int t;
        cin >> t;
        while (t--) {
            cin >> N;
            vector<node> list;
            int i;
            for (i = 0; i < N; i++) {
                node temp;
                cin >> g[i];
                temp.val = g[i];
                temp.index = i;
                list.push_back(temp);
            }
            sort(list.begin(), list.end(), cmp);
            cout << solve(g, list) << endl;
        }
        return 0;
    }
    

    有人可以向我解释一下。我很清楚在nlog(n)中计算LIS。 我无法理解的是这部分:

    int ret = 1;
    for (i = 0; i < min(LIMIT, (int)list.size()); i++) {
        // cout<<list[i].index<<endl;
        ret = max(ret, compute(list[i].index, g));
    }
    

    以及排序背后的原因

    sort(list.begin(),list.end(),cmp);
    

1 个答案:

答案 0 :(得分:0)

这个算法只是猜测起点并计算每个猜测的LIS。

LIS中的第一个值可能是一个小数字,因此该算法只是将LIMIT最小值作为潜在起点。

sort函数用于识别最小值。

for循环用于依次检查每个起点。

警告

请注意,此算法可能会因某些输入而失败。例如,考虑序列

0,1,2,..,49,9900,9901,...,99999,50,51,52,...,9899

该算法将仅尝试前37个起点,并在50处错过最佳起点。

您可以通过将代码更改为:

来测试
int main() {
    int t;
    t=1;
    while (t--) {
    N=10000;
        vector<node> list;
        int i;
        for (i = 0; i < N; i++) {
            node temp;
        if (i<50)
        g[i]=i;
        else if (i<150)
            g[i]=9999-150+i;
        else
            g[i]=i-100;
            temp.val = g[i];
            temp.index = i;
            list.push_back(temp);
        }
        sort(list.begin(), list.end(), cmp);
        cout << solve(g, list) << endl;
    }
    return 0;
}

这将根据LIMIT是37还是370来生成不同的答案。

在实践中,对于随机生成的序列,它将有很好的工作机会(虽然我不知道如何精确计算概率)。