【问题标题】:Swapping values between two vectors so that sum of max_elements of two vectors is minimum在两个向量之间交换值,使两个向量的 max_elements 之和最小
【发布时间】:2016-09-24 01:13:02
【问题描述】:

这是来自 Codechef 的问题,但请多多包涵。 https://www.codechef.com/ZCOPRAC/problems/ZCO16001

该竞赛是为准备在印度举行的区域计算奥林匹克竞赛而举办的,所以它不是我能从中获得收益的竞争性竞赛。只需要一点帮助来看看我的代码有什么问题,因为我觉得我忽略了一些大而愚蠢的事情。 :P

所以基本上问题总结如下。

假设有两个向量或数组。你需要换 它们之间的元素使得它们的最大元素之和 是最小值。但是,您最多可以交换 K 次。然后输出 这个总和的值。

我的方法很简单。取 Vector1 (V1) 中的最高数字,并将其与 V2 中的最低数字交换。添加每个的最高值。做同样的事情,但这次将 V2 中的最高数字与 V1 中的最低数字交换。添加每个的最高值。更好的交换将是总和最小的交换,并从那里继续 K 次。

例如:

V1 = 5 6 7 9

V2 = 9 10 5 4

在这种情况下,如果 K = 1 我首先将 V1 的 9 与 V2 的 4 交换。这给出了:

V1 = 5 6 7 4

V2 = 9 10 5 9

与之前的 19 相比,最高数字的总和为 17。我可以做的第二次交换是 V2 中的 10 与 V1 中的 5:

V1 = 10 6 7 4

V2 = 9 5 5 9

这样总和为 19,所以第一次交换更好,输出应该是 17。

这是我的解决方案:

#include <iostream>
#include <vector>
#include <algorithm>
#define print(vec) for (int i  = 0; i < vec.size(); i++) { cout << vec[i] << " "; } cout << endl;
using namespace std;

vector <long long int> s1, s2;

inline long long int calc(vector <long long int> v1, vector<long long int> v2) {
    return *max_element(v1.begin(), v1.end()) + *max_element(v2.begin(), v2.end());
}
int main(){


    long long int n, k;
    cin >> n >> k;
    long long int x;
    for (unsigned int i = 0; i < n; i++) {
        cin >> x;
        s1.push_back(x);
    }
    for (unsigned int i = 0; i < n; i++) {
        cin >> x;
        s2.push_back(x);
    }
    while (k--) {

        vector <long long int> b1(s1);
        vector <long long int> b2(s2);
        long long int skewb = calc(b1,b2);

        vector <long long int> v1(s1);
        vector <long long int> v2(s2);

        auto mn1 = minmax_element(v1.begin(), v1.end());
        auto mn2 = minmax_element(v2.begin(), v2.end());

        iter_swap(mn1.second, mn2.first);

        b1 = vector <long long int> (v1);
        b2 = vector <long long int> (v2);
        skewb = calc(v1,v2);

        v1 = vector <long long int> (s1);
        v2 = vector <long long int> (s2);
        mn1 = minmax_element(v1.begin(), v1.end());
        mn2 = minmax_element(v2.begin(), v2.end());

        iter_swap(mn2.second, mn1.first);
        if (calc(v1,v2) <= skewb) {
            b1 = vector <long long int> (v1);
            b2 = vector <long long int> (v2);
        }

        if (b1 == s1 && b2 == s2) cout << "LOL" << endl;
        s1 = vector <long long int> (b1);
        s2 = vector <long long int> (b2);
    }



    cout << calc(s1, s2) << endl;
}

请注意,这会进行所有交换,即 K。所以即使当前的安排是最好的,它仍然会交换一些值。早些时候,当当前的安排是最好的时候,我打破了。这样做的原因是因为我得到了所有测试用例,除了两个!猜猜更烦人的是,每个任务都有一个! :( 所以我意识到必须完成所有K个开关。但是,即使现在我弄错了 2 个测试用例,也一定有一些我忽略了的东西。

知道它是什么吗? 解决方案链接:https://www.codechef.com/viewsolution/11574501

顺便说一句,任务 1 的 K = 1。

【问题讨论】:

  • @tobi303,如果你阅读codechef上的声明,你就会明白一切。
  • 对不起,我的意思是 max_element 和 element 而不是长度。我指的是我包含的 Codechef 问题的链接。
  • @AlexStone,请更新问题详情。直觉为什么你的算法是正确的?
  • 你能告诉我你的哪些测试用例(任务#)在 codechef 法官上失败了。
  • @AlexStone 我提供了被接受的实现。但我建议你在阅读解释后自己编码。

标签: c++ algorithm


【解决方案1】:

您的代码的问题是您在交换之间更改了数组,因此有可能在数组之间来回交换一项。我的意思是在第一次交换中,您将元素 x 从 array1 放置到 array2,并且在下一次交换中,您可能会再次将其交换回来。

您还进行了大量矢量复制,这使代码效率低下。即使您的代码逻辑正确,您的代码也不会超过时间限制,因为您的方法是 O(n2)。


首先请注意,最佳答案是当一个数组中的所有元素都大于另一个数组中的所有元素时。

  • 对两个数组进行排序
  • 对于 x 从 0 到 k
    • 假设将第一个数组的 x 个最小元素与第二个数组的 x 个最大元素交换。
    • result = min(result, max(result, max(first array) + max(second array))
    • 假设将第一个数组的 x 个最大元素与第二个数组的 x 个最小元素交换。
    • result = min(result, max(result, max(first array) + max(second array))
  • result 将保留最终答案

由于两个数组都已排序,因此您可以通过一次比较在假设交换后找到数组的最大元素。

V1 = 5 6 7 9 -> 5 6 7 9
V2 = 9 10 5 4 -> 4 5 9 10

x = 0   no swaps: result = V1[size-1] + V2[size-1]
x = 1 
        result = max(V1[size-1], V2[size-1]) + max(V1[0], V2[size-2])
        result = max(V1[size-2], V2[0]) + max(V1[size-1],V2[size-1])
x = 2 
        result = max(V1[size-1], V2[size-1]) + max(V1[1], V2[size-3])
        result = max(V1[size-3], V2[1]) + max(V1[size-1],V2[size-1])
...

这是公认的实现:

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
using namespace std;


int main()
{
    int n,k;
    cin>>n>>k;

    vector<int> v[2];
    for ( int i=0;i < 2;++i ){
        for ( int j=0;j<n;++j ){
            int temp; cin>> temp;
            v[i].push_back(temp);
        }
    }

    sort(v[0].begin(),v[0].end());
    sort(v[1].begin(),v[1].end());

    int result = v[0].back() + v[1].back();

    for ( int i=1; i<=k; ++i ){
        int t = max(v[0][n-1],v[1][n-1]) + max(v[0][i-1],v[1][n-1-i]);
        result = min(result,t);
        t = max(v[0][n-1-i],v[1][i-1]) + max(v[0][n-1],v[1][n-1]);
        result = min(result,t);
    }

    cout << result << endl;
}

【讨论】:

  • 为什么是 max(first array) - max(second array) ?我们希望最高书籍的总和最小。
  • @dd2 提供此算法无法处理的输入。
  • 如果我知道的话,现在应该已经更正了。请在 codechef 上尝试您的解决方案,您会意识到存在一些极端情况。顺便说一句,有些人已经解决了这个问题。
  • @dd2 所以你不知道代码有什么问题,并在我发布答案后 1 分钟投了反对票。具有讽刺意味的是,我提交了我的实现并被接受了。
  • @AlexStone 是的,确实如此。
【解决方案2】:

如果我理解正确,您希望最小化两个最大元素的总和,每个数组一个。您只能进行 K 次交换。

假设数组已排序(这不会改变算法)。 选择按顺序排列的 k 个元素较小的数组。我们称这个数组 S 和另一个数组 B。 在每次交换中,从数组 S 中取出最大的元素并与数组 B 中的最小元素交换。 如果某个时间数组 S 中的所有元素都比 B 中的元素小,则停止。

解释:我们知道两个数组中的最大元素都将在解中。所以我们希望另一个数组具有尽可能小的最大元素。 这就是这个算法的作用。

【讨论】:

  • 来吧,伙计。亚历克斯通过您错过的一项额外检查做了同样的事情,根据 codechef 法官的说法,这是不正确的。我也实施了自己的解决方案,但没有成功。我相信我们可能会遗漏任何极端情况。
  • 是啊。有一个非常奇怪的案例以某种方式想念我。我尝试了很多测试用例,但它们都奏效了。
  • 再次阅读我的解决方案,它与亚历克斯不同。否决我是不公平的
【解决方案3】:

这个问题的逻辑是 对于 K=1,最低偏差将是最大值(假设在 V1 中找到)+ 另一个向量的第二大值(称为 V2)

我已经在下面给出了方法

对于 K=1,你交换一个,现在你得到两个新向量,你再次计算 K=1,这意味着如果你知道如何获得 k=1 的最小偏差,你可以重复相同的步骤 k 次,所以下面我给出了找到最小偏差的步骤--->

有两个向量V1和V2

Step 1 --> 找到所有元素中最大的,假设它在V1中

步骤 2 --> 找到另一个向量的第二大元素,即在这种情况下(V2 的第二大元素)假设它的名字是 e2

Step 3 -->找到最大的V2假设它的名字是e1

第 4 步 --> 将 V2 的最大元素,即 e1 与 V1 的元素交换(其中 V1 的元素小于 e2)

以上步骤适用于 k=1 如果你重复这些步骤,你可以得到最低的偏斜

例如有问题-->

V1 --> 1 14 2 3 10 4

V2---> 5 1 3 5 2 7

找到最大的--> V1 中的 14 个

因为最大的元素在 V1 中,所以找到 V2 的第二大元素 --> 它的 5

找到最大的 V2 ---> 其 7

将 V2 的 7 个与 V1 的 3 个交换(小于 5 个)

计算偏斜

重复步骤

【讨论】:

  • 本质上,你正在做我正在做的事情,但是用尽了计算时间来找到另一个向量的第二大元素,而你什么也没做。
  • @ALexStone 我们必须找到另一个向量的第二大元素,以便可以用小于第二大元素的值替换最大元素,其纯粹的数学和能力逻辑,以便获得最低的偏斜
  • 我认为这行不通。在此之前我尝试了类似的解决方案。而且我没有对你投反对票。
  • @AlexStone 好的,所以基本逻辑是 K=1,最低偏斜将是所有向量中的最大值 + 另一个向量的第二大值,现在您将重复一遍,我刚刚让路这样做
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-22
  • 1970-01-01
  • 2013-12-04
  • 1970-01-01
  • 2013-02-03
  • 1970-01-01
  • 2015-11-24
相关资源
最近更新 更多