间隔数字猜测游戏

时间:2014-02-17 06:40:40

标签: java algorithm range

我刚开始在CodeChef上成为更好的编码器。人们从标记为“简单”的问题开始,我也做了同样的事情。

The Problem

问题陈述定义了以下内容:

  1. n,其中1 <= n <= 10^9。这是约翰尼保密的整数。
  2. k,其中1 <= k <= 10^5。对于每个测试用例或游戏实例,Johnny向Alice提供了k提示。
  3. 提示的格式为op num Yes/No,其中 -
    • op是来自<>=的运营商。
    • num是一个整数,再次满足1 <= num <= 10^9
    • YesNo是问题的答案:关系n op num是否成立?
  4. 如果问题的答案是正确的,约翰尼说出了一个真相。否则,他在说谎。
  5. 每个提示都被输入程序,程序确定它是真实还是谎言。我的工作是找到尽可能少的谎言。

    现在CodeChef's Editorial answer使用了段树的概念,我根本无法绕过它。我想知道是否有一个替代数据结构或方法来解决这个问题,可能更简单一个,考虑到它是在“简单”类别。

    这是我试过的 - :

    class Solution //Represents a test case.
    {
        HashSet<SolutionObj> set = new HashSet<SolutionObj>(); //To prevent duplicates.
        BigInteger max = new BigInteger("100000000"); //Max range.
        BigInteger min = new BigInteger("1"); //Min range.
        int lies = 0; //Lies counter.
    
        void addHint(String s)
        {
            String[] vals = s.split(" ");
            set.add(new SolutionObj(vals[0], vals[1], vals[2]));
        }
    
        void testHints()
        {
            for(SolutionObj obj : set)
            {
                //Given number is not in range. Lie.
                if(obj.bg.compareTo(min) == -1 || obj.bg.compareTo(max) == 1)
                {
                    lies++;
                    continue;
                }
                if(obj.yesno)
                {
                    if(obj.operator.equals("<"))
                    {
                        max = new BigInteger(obj.bg.toString()); //Change max value
                    }
                    else if(obj.operator.equals(">"))
                    {
                        min = new BigInteger(obj.bg.toString()); //Change min value
                    }
                }
                else
                {
                    //Still to think of this portion.
                }
            }
        }
    
    }
    
    class SolutionObj //Represents a single hint.
    {
        String operator;
        BigInteger bg;
        boolean yesno;
    
        SolutionObj(String op, String integer, String yesno)
        {
            operator = op;
            bg = new BigInteger(integer);
            if(yesno.toLowerCase().equals("yes"))
                this.yesno = true;
            else
                this.yesno = false;
        }
    
        @Override
        public boolean equals(Object o)
        {
            if(o instanceof SolutionObj)
            {
                SolutionObj s = (SolutionObj) o; //Make the cast
                if(this.yesno == s.yesno && this.bg.equals(s.bg)
                        && this.operator.equals(s.operator))
                    return true;
            }
                return false;
        }
    
        @Override
        public int hashCode()
        {
            return this.bg.intValue();
        }
    }
    

    显然这个部分解决方案不正确,除了我在进入if(obj.yesno)部分之前完成的范围检查。我正在考虑根据提供的提示更新范围,但这种方法并未取得成果。除了使用分段树之外,我该如何处理这个问题?

3 个答案:

答案 0 :(得分:3)

考虑以下方法,这可能更容易理解。画出整数的1d轴,并在其上放置k个提示。每个提示都可以被视为'('或')'或'='(分别大于,小于或等于)。

示例:

-----(---)-------(--=-----)-----------)

现在,真值是在这个轴的40个值中的某个值上,但实际上只有8个段值得检查,因为在段内的任何位置,真/假提示的数量保持不变。 这意味着您可以根据轴上的顺序扫描提示,并在该点保持真实提示的计数器。

在上面的示例中,它是这样的:

segment         counter
-----------------------
-----(             3
---                4
)-------(          3
--                 4
=                  5 <---maximum
-----              4
)-----------       3
)                  2 

此算法仅需要对k提示进行排序,然后扫描它们。它在k(O(k * log k)附近是线性的,不依赖于n),因此它应该具有合理的运行时间。

备注:

1)在实践中,提示可能有不同的位置,因此您必须在同一位置处理所有相同类型的提示。

2)如果你需要返回最小的谎言集,那么你应该维护一个集合而不是一个计数器。如果您使用哈希集,这不应该对时间复杂性产生影响。

答案 1 :(得分:0)

如果目标数量= 1,则计算谎言数量(将其存储在变量lies中)。

target = 1。

按语句的各自值对语句进行排序和分组。

迭代声明。

  • target更新为当前对帐单组的值。根据这些陈述中有多少是真或假来更新lies

  • 然后将target更新为该值+ 1(为什么这样做?考虑何时> 5< 7 - 6可能是最佳值?并适当更新lies(如果下一个语句组的值为此值,则跳过此步骤。)

返回lies的最小值。

运行时间:

O(k)进行初步计算。

O(k log k)用于排序。

O(k)用于迭代。

O(k log k)总计。

答案 2 :(得分:0)

我对这个问题的看法与Eyal Schneider的观点类似。表示'&gt;'更大,'&lt;'如果小于和'='等于,我们可以按num对所有'提示'进行排序,并逐个扫描所有有趣的点。

对于每一点,我们保留所有'&lt;'的数量和'='从0到那一点(在一个名为int[]lessAndEqual的数组中),'&gt;'的数量和从那一点开始的'='(在一个名为int[]greaterAndEqual的数组中)。我们可以很容易地看到特定点i中的谎言数量等于

lessAndEqual[i] + greaterAndEqual[i + 1]

我们可以通过O(n)中的两次扫描轻松填充lessAndEqualgreaterAndEqual数组,并对O(nlogn)中的所有提示进行排序,结果时间复杂度为O(nlogn)< / p>

注意:当提示中的num等于时,应采取特殊处理。另请注意num的范围是10 ^ 9,这要求我们使用某种形式的点压缩来使数组适合内存