可能比O(n ^ 2)时间更好吗?

时间:2017-02-08 15:42:18

标签: c# algorithm linq data-structures

我试图解决的问题给了我一个像

这样的矩阵
10101
11100
11010
00101

这些行应该代表一个人知道的主题;例如由10101代表的人1知道主题1,3和5,但不知道2或4.我需要找到2人团队可以知道的最大主题数;例如第1人和第3人的团队知道所有主题,因为在1010111010之间,每个索引都有1个。

我有一个O(n ^ 2)解决方案

    string[] topic = new string[n];
    for(int topic_i = 0; topic_i < n; topic_i++)
    {
       topic[topic_i] = Console.ReadLine();   
    }
    IEnumerable<int> teamTopics = 
        from t1 in topic
        from t2 in topic
        where !Object.ReferenceEquals(t1, t2)
        select t1.Zip(t2, (c1, c2) => c1 == '1' || c2 == '1').Sum(b => b ? 1 : 0);
    int max = teamTopics.Max();
    Console.WriteLine(max);

通过所有测试用例,它不会超时。我怀疑它不够快的原因与时间复杂性有关,而不是LINQ机器的开销。但我无法想出更好的方法。

我想也许我可以将主题索引映射到知道它们的人,比如

1 -> {1,2,3}
2 -> {2,3}
3 -> {1,2,4}
4 -> {3}
5 -> {1,4}

但我无法想到从哪里开始。

你能给我一个&#34;提示&#34;?

3 个答案:

答案 0 :(得分:3)

我们说我们有n个人和m个主题。

我认为你的算法是O(n ^ 2 * m),其中n是人数,因为:

  1. from t1 in topic让你O(n)
  2. from t2 in topic将您带到O(n ^ 2)
  3. t1.Zip(t2 ...让你到O(n ^ 2 * m)
  4. 我看到的优化是首先修改字符串:

    • s1 =&#39; 0101&#39;,其中第i个元素显示我是否知道第一个主题的人
    • s2 =&#39; 1111&#39;,其中第i个元素显示我是否知道第二个主题的人。

    等...

    然后你分析字符串s1。您选择所有可能的1对(O(n^2)元素),这些元素显示了一起知道第一个主题的人对。然后从该列表中选择一对并检查他们是否也知道第二个主题,依此类推。当他们不在时,将其从列表中删除并转到另一对。

    不幸的是,这看起来也是O(n^2 * m),但实际上这应该更快。对于非常稀疏的矩阵,它应该接近O(n 2 ),对于密集矩阵,它应该很快找到一对。

答案 1 :(得分:2)

思想:

  • 作为一种推测性优化:你可以进行O(n)扫描以找到技能最多的人(最大汉明重量);注意它们,如果他们拥有一切就停下来:与任何人配对,没关系
  • 您可以排除任何未经测试的人与“最佳”个人熟练分享 - 我们已经知道他们可以提供的所有内容并且已经针对每个人进行了测试;所以只测试(newSkills & ~bestSkills) != 0 - 意思是:被测试的人有“最好的”工人没有的东西;这使得具有互补技能的工人加上“最佳”工人(你必须明确包括他们,因为上面的~ / !=0测试将失败)
  • 现在再次对可能的合作伙伴进行O(m)扫描 - 检查“最熟练”加上任何其他技能是否能为您提供所有技能(如果单个成员拥有所有技能,显然会提前停止);但无论哪种方式:跟踪最佳组合以供日后参考
  • 你可以通过只考虑三角形来进一步时间,而不是方形 - 意思是:你将行0与行1 - (m-1)进行比较,但行12 - (m-1),行56 - (m-1)
  • 你可以通过使用整数位数学和有效的“汉明重量”算法(计算设定位)而不是字符串和求和来显着改善事物
  • 摆脱LINQ
  • 如果你得到所有的那个就会短路(与~((~0)<<k)比较,其中k是要测试的位数)
  • 记得将任何结果与我们针对最熟练的工人发现的“最佳”组合进行比较

这仍然是O(n) + O(m^2),其中m <= n是技能技能最高的人

的人数

病理但技术上正确的答案:

插入Thread.Sleep(FourYears) - 所有解决方案现在基本上都是O(1)

答案 2 :(得分:2)

您的解决方案渐近有效,因为您需要检查所有对以达到最大值。您可以通过使用BitArray对象替换字符串来提高代码效率,如下所示:

var topic = new List<BitArray>();
string line;
while ((line = Console.ReadLine()) != null) {
    topic.Add(new BitArray(line.Select(c => c=='1').ToArray()));
}
var res =
   (from t1 in topic
    from t2 in topic
    select t1.Or(t2).Count).Max();
Console.WriteLine(res);

Demo.