我刚开始在CodeChef上成为更好的编码器。人们从标记为“简单”的问题开始,我也做了同样的事情。
问题陈述定义了以下内容:
n
,其中1 <= n <= 10^9
。这是约翰尼保密的整数。k
,其中1 <= k <= 10^5
。对于每个测试用例或游戏实例,Johnny向Alice提供了k
提示。op num Yes/No
,其中 -
op
是来自<
,>
,=
的运营商。num
是一个整数,再次满足1 <= num <= 10^9
。Yes
或No
是问题的答案:关系n op num
是否成立? 每个提示都被输入程序,程序确定它是真实还是谎言。我的工作是找到尽可能少的谎言。
现在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)
部分之前完成的范围检查。我正在考虑根据提供的提示更新范围,但这种方法并未取得成果。除了使用分段树之外,我该如何处理这个问题?
答案 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)中的两次扫描轻松填充lessAndEqual
和greaterAndEqual
数组,并对O(nlogn)中的所有提示进行排序,结果时间复杂度为O(nlogn)< / p>
注意:当提示中的num
等于时,应采取特殊处理。另请注意num的范围是10 ^ 9,这要求我们使用某种形式的点压缩来使数组适合内存