Codeforces问题的正确性证明:拳击手(额定1500)

时间:2019-09-18 12:41:39

标签: algorithm proof proof-of-correctness

考虑此问题出现在Codeforce中(等级1500):

  

有n个拳击手,第i个拳击手的权重是ai。他们每个人都可以在比赛前将权重改变不超过1(权重不能等于0,也就是说,它必须保持正值)。重量始终是整数。

     

有必要根据人数来选择最大的拳击团队,以确保团队中所有拳击手的权重都不同(即唯一)。

     

编写一个程序,对于给定的当前值,ai将在团队中找到最大可能的拳击手数量。

     

在进行一些更改之后,某些拳击手的重量可能为150001(但没有更多)。

     

输入   :第一行包含整数n(1≤n≤150000)-拳击手的数量。下一行包含n个整数a1,a2,...,an,其中ai(1≤ai≤150000)是第i个拳击手的权重。

     

输出   :打印一个整数,即一个团队中的最大人数。

这是我实现的代码,已被Codeforces接受为正确的提交内容(Python 3.7):

n=int(input())
inputlist=list(map(int, input().split()))
inputlist.sort()
boxerset=set()
for i in inputlist:
    if i-1 not in boxerset and i!=1:
        boxerset.add(i-1)
    elif i not in boxerset:
        boxerset.add(i)
    elif i+1 not in boxerset:
        boxerset.add(i+1)
    else:
        continue
print(len(boxerset))

但是,我无法严格证明该算法必须给出正确的答案。例如,当inputlist为[1、1、1、2、2、5、8]时,[1、2、3、4、7、8](我的算法)和[1、2、3、6, 7,8]是正确的输出boxerset,尽管两种情况的答案都是6。

我的问题是这个

如何证明我的算法正确?我的证明将如何工作,以表明在所有可能的合法拳击手选择中,我的算法所进行的选择具有最大的拳击手数量(这就是我如何证明我不存在其他选择的拳击手数量)大于我的算法所做的选择)?

我曾尝试通过矛盾证明,但无济于事(尽管我认为证明必须具有通过矛盾证明的自然结构)。

https://codeforces.com/problemset/problem/1203/E

2 个答案:

答案 0 :(得分:1)

您的代码可以视为专门的动态程序。

首先,一个结构定理:存在一个最佳解决方案,其中每对组成团队的起始权重为a_i < a_j的拳击手,拳击手i的战斗权重小于拳击手j。这是因为发生这种情况的唯一方法是,如果a_i + 1 = a_ji的战斗力是a_i + 1,而j的战斗力是a_j - 1。但是,在这种情况下,我们可以让ij只是按照它们的初始权重进行战斗,而最终团队将具有相同的权重集。

鉴于此结构定理,有一个动态程序按从最轻到最重的起始重量顺序考虑每个拳击手。最佳子结构是,对于按排序顺序排列的拳击手而言,两个相关参数是:1.从前缀中选择的拳击手数量; 2.组成团队的拳击手最大的战斗力(由于结构定理) ,我们可以假设每个后续拳击手的战斗力都必须更大)。因此,动态程序应跟踪每个战斗重量限制的最大团队规模。

我们观察到可以删除一些解决方案,而不是写出该DP。特别是,如果有k个拳击手的子解决方案,其权重为w,而另一个子解决方案是k-1,其权重为w-1,那么就没有意义了第二,因为当我们添加另一个拳击手的那一刻,它不会比第一个更好。另一方面,也许我们有一个带有k拳击手且权重为w的子解决方案和一个带有k-1拳击手且权重为w' < w-1的子解决方案。如果没有拳击手的重量小于w,那么我们可以再次放弃第二个子解决方案。经过一些案例分析,结果是我们永远不必记住一个以上的子解决方案。

循环的最终版本如下所示。

maxweightonteam = 0
countonteam = 0
for ai in inputlist:
    if ai < maxweightonteam:
        continue
    assert ai + 1 >= maxweightonteam + 1
    maxweightonteam = max(maxweightonteam + 1, ai - 1)
    countonteam += 1
print(countonteam)

就像您的代码一样,此解决方案反复贪婪地选择了下一个最轻的拳击手可以战斗的最小重量。两者都能产生最佳结果。

答案 1 :(得分:0)

David Eisenstat在回答第二段的最后一句话“因此,动态程序应跟踪每个战斗重量限制的最大团队规模”,这启发了我一个替代解决方案。我使用归纳法:

假设在循环中进行迭代时,所选战斗机的最大战斗重量为 k

  

归纳假设是此子序列由权重约束 k 的最大团队规模组成。

     

作为归纳步骤,当下一次迭代增加较高的战斗权重时,这显然是重量限制为 k + 1 的最大团队规模。

要看到这种情况,请假设情况并非如此。然后,存在一个子序列,该子序列的团队规模要比根据我们的算法创建的子序列更大。从此列表中删除 k + 1 战斗机。那么此列表应该是权重约束 k 的最大子序列,这会导致归纳假设的矛盾。因此证明了该算法的正确性。

唯一未包括在内的情况是,拳击手名单中只有 1 个可能的战斗机。这种情况是微不足道的,因此证明是完整的。