【问题标题】:An algorithm Time limit exceeded超出算法时间限制
【发布时间】:2021-05-29 04:48:43
【问题描述】:

话题链接:https://codeforces.com/contest/109/problem/D

对于这个问题,我在 test41 上超过了时间限制

我猜是用的数据结构不合适,但是想了很久不知道哪里可以采用好的数据结构,请大家给点建议。

算法思路:

这种方法有点类似于选择排序。先找一个幸运数字,然后和第一个数字交换位置,然后从第二个开始,找到最小的数字,然后和幸运数字交换,这样从第二个开始的最小的数字放在第一个位置,幸运数字现在在另一个位置,它的位置被记录下来。 然后将幸运数字与第二个数字交换,然后重复上述过程。需要特别注意的是,我们需要在排序后记录我们选择的幸运数字的位置,因为位置前后的过程略有不同,需要进行比较(这在代码cmets中有说明)。


using namespace std;
typedef long long ll;

// Judge whether it is a lucky number. if it is a lucky number, return true, otherwise, return false.
bool judge(string x);


bool static cmp(vector<ll> a, vector<ll> b) { return a[0] < b[0]; }

int main()
{
    int n;
    cin >> n;
    vector<ll> nums;
    ll smLuckyNum = INT64_MAX;
    for(int i = 0; i < n; i++)
    {
        ll x;
        cin >> x;
        nums.push_back(x);
        if(judge(to_string(x)) && smLuckyNum > x) smLuckyNum = x;  // Find the smallest lucky number
    }

    vector<vector<ll> > b;  // b[i][0] stores nums[i], b[i][1] stores i index

    int pos, origIndex;
    bool sorted = true;
    
    // b[i][0] stores nums[i] and b[i][1] stores i index
    for(int i = 0; i < n; i++)
    {
        vector<ll> tmp;
        tmp.push_back(nums[i]);
        tmp.push_back(i);
        b.push_back(tmp);
    }

    sort(b.begin(), b.end(), cmp);  // sort by b[i][0]

    // If the original array is ordered, sorted == true, otherwise, sorted == false
    for(int i = 0; i < n; i++)
        if(b[i][1] != i) sorted = false;

    // If the original array is not ordered and there is no lucky number in it, no way to sort, return -1
    if(!sorted && smLuckyNum == INT64_MAX) { cout << -1 << endl; return 0; }
    // If the original array is ordered, no sorting required
    if(sorted) { cout << 0 << endl; return 0; }

    // Find the position of the smallest lucky number, origIndex is the index of the smLuckyNum before sorting, pos is the index of the smLuckyNum after sorting
    for(int i = 0; i < n; i++)
    {
        if(smLuckyNum == b[i][0])
        {
            origIndex = b[i][1];
            pos = i;
            break;
        }
    }

    vector<string> ans;
    // Traverse the nums array from 0 to n - 2, if  i is equal to pos(the index of smLuckyNum after sorting)  , continue.
    // We can swap the element at position i and the element at position origIndex, and record the position of LuckyNum after the swap in origIndex,
    // then find the smallest element from i+1 to n-1 and record the index in MINID, notice if i > pos, and  nums[MINID] > nums[pos] (because there may be a large number placed at pos), the element at pos is smallest number
    // if MINID < n, swap(nums[i](it is a lucky number), nums[MINID])
    for(int i = 0; i < n - 1; i++)
    {
        if(i == pos) continue;

        string res = "";
        if(i != origIndex)
        {
            res += (to_string(i + 1) + " " + to_string(origIndex + 1)); // According to the requirement of the question, the index starts from 1, the result needs to plus one.
            swap(nums[i], nums[origIndex]);
            origIndex = i;
            ans.push_back(res);
        }

        ll MINID = i + 1;
        for(int j = i + 1; j < n; j++)
            if(nums[MINID] > nums[j]) MINID = j;

        if(i > pos && nums[MINID] > nums[pos]) MINID = pos;
        if(MINID < n)
        {
            res = "";
            res += (to_string(i + 1) + " " + to_string(MINID + 1));
            swap(nums[i], nums[MINID]);
            origIndex = MINID;
            ans.push_back(res);
        }
    }

    // Finally we need to compare nums[n - 1] with nums[pos] when pos != n - 1
    if(pos + 1 != n && nums[pos] > nums[n - 1])
        ans.push_back(to_string(pos + 1) + " " + to_string(n));

    cout << ans.size() << endl;
    
    for(int i = 0; i < ans.size(); i++)
        cout << ans[i] << endl;
    
    return 0;
}


bool judge(string x)
{
    for(int i = 0; i < x.size(); i++)
        if(x[i] != '4' && x[i] != '7') return false;
    return true;
}

【问题讨论】:

    标签: algorithm data-structures


    【解决方案1】:

    用于查找 MINID 的嵌套循环使运行时在 n 中成为二次方:

    for(int j = i + 1; j < n; j++)
        if(nums[MINID] > nums[j]) MINID = j;
    

    当 n = 100,000 时,这将花费很长时间。你需要找到一种更好的方法来计算MINID

    提示:您在开始时准备b[],然后永远不要使用它。

    【讨论】:

    • 是的,我本来想在这个地方使用priority_queue或者set,但是当你把幸运数字和位置i的元素交换时,这样做会改变b[i][1],所以你必须遍历树找到这棵树中的b[i][0]来改变它的b[i][1],特别是在处理i>pos的情况时,不能做简单的pop。逻辑太复杂,写不出来。你能用更聪明的方式指导我吗?
    • 这里有一个提示:b[i][1] 已经告诉您哪个元素需要在位置 i 结束。假设到目前为止,我们已经将最小元素移动到数组中的位置 0,而“枢轴”幸运元素位于位置 100。要将第二小的元素移动到位置,我们希望交换位置 1 和 100,然后是位置 1和b[1][1]——但我们不能确定第二小的元素仍然在位置b[1][1],因为我们的交换可能已经干扰了它。但是,如果我们有第二个数组 c[i] 告诉我们最初位于位置 i 的元素的 当前 位置呢?
    • 我大概知道你的意思了。你的意思是我不需要写一个 for 循环来找到最小的数字。我可以使用数组c来跟踪元素的移动,使用b数组直接获取当前应该放置的数字。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-03
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多