竞争性编程 - 代码为在线编译器提供了不同的答案

时间:2018-03-21 16:26:50

标签: c++ dynamic-programming

我试图解决problem 811C on codeforces。我开始编写sublime-text编码,并设法在一段时间后提出解决方案。当我运行该程序时,它给了我正确的答案但是当我提交代码时,出于某种原因我得到了代码强制的不同答案。我检查数组是否超出范围,但这似乎不是原因。这是代码:

/* Code Readability Credit : Max Vollmer */
#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <set>

// terms[] stores the value of all the terms. leftMosts[i] and rightMosts[i] store the leftmost and rightmost occurrences of the ith element in terms[]. 
//solvedValues[] stands for dynamic programming and stores the pre calculated terms of the function solve()
int numberOfTerms;
int terms[5005];
int leftMosts[5005];
int rightMosts[5005];
int solvedValues[5005];

int comf(int left,int right)// comf = comfort (see problem statement if you do not understand this)
{ 
    std::set<int> track;
    int ret = 0;
    for(int i = left; i <= right; i++)
    {
        if (!track.count(terms[i]))
        {
            ret = (ret^terms[i]);
            track.insert(terms[i]);
        }
    }
    return ret;
}

// below, solve stands for 'solve', it is terms recursive memoized function
// returns max sequence from i to (numberOfTerms-1). to find max, call solve(0, -1) so i=0,lefmost=-1. leftmost keeps track of the position of last index of last chosen set.
int solve(int i, int leftmost)
{
    if (i >= numberOfTerms)
    {
        return 0;
    }

    if (solvedValues[i] != -1)
    {
        return solvedValues[i];
    }

    if (leftMosts[i] <= leftmost)
    {
        return solve(i+1, leftmost);// we cant go choose leftMosts[i] to rightMosts[i] so we move on
    }

    // decide if it is better to choose current leftMosts[i] to rightMosts[i] or better to simply move on and skip this.
    return solvedValues[i] = std::max(comf(leftMosts[i], rightMosts[i]) + solve(rightMosts[i]+1, rightMosts[i]), solve(i+1, leftmost));
}

void init()
{
    scanf("%d", &numberOfTerms);
    for(int i = 0; i < numberOfTerms; i++)
    {
        scanf("%d", &terms[i]);
    }
    // init all as -1
    memset(solvedValues, -1, sizeof(solvedValues));
    memset(leftMosts, -1, sizeof(leftMosts));
    memset(rightMosts, -1, sizeof(rightMosts));
}

int main()
{
    init();

    // calc leftMosts[i] and rightMosts[i] for all 'i in terms
    for (int i = 0; i < numberOfTerms; i++)
    {
        for (int leftIndex = 0; leftIndex < i; leftIndex++)
        {
            if (terms[leftIndex] == terms[i])
            {
                leftMosts[i] = leftIndex;
                break;
            }
        }

        for (int rightIndex = numberOfTerms-1; rightIndex > i; rightIndex--)
        {
            if (terms[rightIndex] == terms[i])
            {
                rightMosts[i] = rightIndex;
                break;
            }
        }

        if (leftMosts[i] == -1)
        {
            leftMosts[i] = i;// if there is no leftmost occ, then leftmost is current
        }

        if (rightMosts[i] == -1)
        {
            rightMosts[i] = i;// same as above for rightmost
        }
    }

    printf("%d\n", solve(0, -1));

    return 0;
}

以下是测试用例:

  

100 931 4584 2116 3004 3813 62 2819 2998 2080 4906 3198 2443 2952 3793 1958 3864 3985 3169 3134 4011 4525 995 4163 308 4362 1148 4906 3092   1647 244 1370 1424 2753 84 2997 1197 2606 425 3501 2606 683 4747 3884 4787 2166 3017 3080 4303 3352 1667 2636 3994 757 2388 870 1788 988   1303 0 1230 1455 4213 2113 2908 871 1997 3878 4604 1575 3385 236 847 2524 3937 1803 2678 4619 1125 3108 1456 3017 1532 3845 3293 2355   2230 4282 2586 2892 4506 3132 4570 1872 2339 2166 3467 3080 2693 1925 2308

正确的输出应该是:

  

227685

Codeforces上的错误输出:

  

245849

同样,在我的机器上,代码工作正常并输出227685但是当它在线运行时,代码输出245849由于某种原因。 代码可以是tested online here. 这是一个image of the code working on a local machine.

我很想知道造成这种情况的原因。

更新 导致此错误的原因是,当最终计算值设置为leftmost时,函数solve(int i,int leftmost)中的变量solvedValues[i]未被考虑在内。这是一个错误/不正确的问题解决方法,会导致std::max()中的顺序影响代码输出等问题。

1 个答案:

答案 0 :(得分:5)

不同结果的原因

您在std::max中使用的参数将在线和本地计算机上以不同的顺序执行。在comf(leftMosts[i], rightMosts[i]) + solve(rightMosts[i]+1, rightMosts[i])之前执行solve(i+1, leftmost)时,您会获得所需的结果。如果交换订单,则会得到错误的结果。

重构的代码

这是我重构的代码,以提高可读性。我所做的其中一件事就是在slv中打破长回报。如果您切换行int a =...int b =...的顺序,则会得到错误的结果:

#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <set>

// terms[] stores the value of all the terms. leftMosts[i] and rightMosts[i] store the leftmost and rightmost occurrences of the ith element in terms[]. 
//solvedValues[] stands for dynamic programming and stores the pre calculated terms of the function solve()
int numberOfTerms;
int terms[5005];
int leftMosts[5005];
int rightMosts[5005];
int solvedValues[5005];

int comf(int left,int right)
{ 
    std::set<int> track;
    int ret = 0;
    for(int i = left; i <= right; i++)
    {
        if (!track.count(terms[i]))
        {
            ret = (ret^terms[i]);
            track.insert(terms[i]);
        }
    }
    return ret;
}

// below, solve stands for 'solve', it is terms recursive memoized function
// returns max sequence from i to (numberOfTerms-1). to find max, call solve(0, -1) so i=0,lefmost=-1. leftmost keeps track of the position of last index of last chosen set.
int solve(int i, int leftmost)
{
    if (i >= numberOfTerms)
    {
        return 0;
    }

    if (solvedValues[i] != -1)
    {
        return solvedValues[i];
    }

    if (leftMosts[i] <= leftmost)
    {
        return solve(i+1, leftmost);// we cant go choose leftMosts[i] to rightMosts[i] so we move on
    }

    // decide if it is better to choose current leftMosts[i] to rightMosts[i] or better to simply move on and skip this.
    int a = comf(leftMosts[i], rightMosts[i]) + solve(rightMosts[i]+1, rightMosts[i]);
    int b = solve(i+1, leftmost);
    return solvedValues[i] = std::max(a, b);
}

void init()
{
    scanf("%d", &numberOfTerms);
    for(int i = 0; i < numberOfTerms; i++)
    {
        scanf("%d", &terms[i]);
    }
    // init all as -1
    memset(solvedValues, -1, sizeof(solvedValues));
    memset(leftMosts, -1, sizeof(leftMosts));
    memset(rightMosts, -1, sizeof(rightMosts));
}

int main()
{
    init();

    // calc leftMosts[i] and rightMosts[i] for all 'i in terms
    for (int i = 0; i < numberOfTerms; i++)
    {
        for (int leftIndex = 0; leftIndex < i; leftIndex++)
        {
            if (terms[leftIndex] == terms[i])
            {
                leftMosts[i] = leftIndex;
                break;
            }
        }

        for (int rightIndex = numberOfTerms-1; rightIndex > i; rightIndex--)
        {
            if (terms[rightIndex] == terms[i])
            {
                rightMosts[i] = rightIndex;
                break;
            }
        }

        if (leftMosts[i] == -1)
        {
            leftMosts[i] = i;// if there is no leftmost occ, then leftmost is current
        }

        if (rightMosts[i] == -1)
        {
            rightMosts[i] = i;// same as above for rightmost
        }
    }

    printf("%d numberOfTerms", solve(0, -1));

    return 0;
}

我们从中学到了什么?

可读,干净的代码不仅对你的程序员(和你自己)很好,还可以降低bug的风险。