我正在尝试使用C#解决11321 - Sort! Sort!! and Sort!!!,直接从cpp和Java转换我的解决方案(以及其他人的解决方案)。
我的问题是listName.Sort()
或Array.Sort(listName, ...Comparer.Create()...)
在第一遍过程中没有正确地对输出进行排序。我必须调用它两次才能对其进行正确排序。
在某些情况下,我在调用Array.Sort时在CompareTo()中手动设置断点,有意地在闭包内部添加了对列表的引用,这样我就可以观察到值的排序情况,并且可以正确排序直到Array.Sort()方法返回,然后在其中看到一些值返回不正确的顺序。
我正在使用Morass' test cases from uDebug进行测试,并且我得到的一个不正确的排序结果示例在输出的第10919行上:
Accepted My Output
10919 457 10919 461
10920 461 10920 457
如您所见,数字461和457应该按其模500值的升序排序,分别为461和457。如果我再次在下面的代码中再次调用sort方法,那么我最终将获得正确的输出。
我想我的问题是,为什么会这样?我的实现有什么问题吗?我的实现几乎是接受的Java或cpp代码的一对一翻译。请注意,我还尝试过使用LINQ的OrderBy(),它会产生不同的结果,但在调用足够的次数时最终会得出正确的结果。
我具有以下Number类以及相应的IComparable实现:
class Number : IComparable<Number>
{
public int Value { get; }
public int Mod { get; }
public bool IsOdd { get; }
public Number(int val, int mod)
{
Value = val;
Mod = mod;
IsOdd = val % 2 != 0;
}
public int CompareTo(Number other)
{
var leftVal = Value;
var leftMod = Mod;
var rightVal = other.Value;
var rightMod = other.Mod;
var leftOdd = IsOdd;
var rightOdd = other.IsOdd;
if (leftMod < rightMod) return -1;
else if (leftMod > rightMod) return 1;
else
{
if (leftOdd && rightOdd)
{
return leftVal > rightVal ? -1 : 1;
}
else if (!leftOdd && !rightOdd)
{
return leftVal > rightVal ? 1 : -1;
}
else if (leftOdd)
{
return -1;
}
else// (rightOdd)
{
return 1;
}
}
}
}
还有我的主要方法:
public static void Main(string[] args)
{
while (true)
{
var settings = Console.ReadLine().Split(' ');
var N = int.Parse(settings[0]);
var M = int.Parse(settings[1]);
if (N == 0 && M == 0) break;
Console.WriteLine($"{N} {M}");
var output = new List<Number>();
var i = 0;
while (i < N)
{
var line = Console.ReadLine();
var val = int.Parse(line);
var mod = val % M;
output.Add(new Number(val, mod));
i++;
}
output.Sort();
// uncomment to produce acceptable answer
// output.Sort();
foreach (var line in output)
{
Console.WriteLine(line.Value);
}
}
Console.WriteLine("0 0");
}
编辑1:
请注意,我正在将stdin和stdout从文件/重定向到StringBuilder,这样我就可以自动化测试。
static void Main(string[] args)
{
var builder = new StringBuilder();
var output = new StringWriter(builder);
Console.SetOut(output);
var solution = File.ReadAllText("P11321_Outputs");
var problem = new StreamReader("P11321_Inputs");
Console.SetIn(problem);
P11321_1.Main(args);
}
修改2: 这是测试案例中发生奇怪行为的一部分。具体的复制步骤是,如果将测试用例更改为仅包含38个项目,并从输入中删除11个,则正确排序为457和461。
输入:
39 500
-121
582
163
457
-86
-296
740
220
-867
-333
-773
11
-446
-259
-238
782
461
756
-474
-21
-358
593
548
-962
-411
45
-604
-977
47
-561
-647
926
578
516
382
-508
-781
-322
712
0 0
输出:
39 500
-977
-474
-962
-446
-411
-867
-358
-333
-322
-296
-781
-773
-259
-238
-647
-121
-604
-86
-561
-21
-508
11
516
45
47
548
578
582
593
163
712
220
740
756
782
382
926
457
461
0 0
答案 0 :(得分:4)
您设法检查了布尔测试中的所有情况,除非值相等。一种排序算法不仅需要知道元素是否大于或小于彼此,而且还需要知道它们是否彼此相等。
if (leftMod < rightMod)
return -1;
else if (leftMod > rightMod)
return 1;
else
{
if (leftVal == rightVal)
{
return 0; // need this so you don't orphan an element when tested against itself
}
if (leftOdd && rightOdd)
{
return leftVal > rightVal ? -1 : 1;
}
else if (!leftOdd && !rightOdd)
{
return leftVal > rightVal ? 1 : -1;
}
else if (leftOdd)
{
return -1;
}
else// (rightOdd)
{
return 1;
}
}
答案 1 :(得分:1)
我将Mark Benningfield的答案加倍,想提出一个概念证明,说明为什么在自定义比较器的实现中包括平等很重要。不仅存在错误结果的风险,而且还存在从未获得结果的风险!
尝试使用越野车比较器仅对两个数字(2、1)进行排序:
class BuggyComparer : IComparer<int>
{
public int Compare(int x, int y) => x < y ? -1 : 1; // Equality?
}
var source = new int[] { 2, 1 };
var sorted = source.OrderBy(n => n, new BuggyComparer());
Console.WriteLine(String.Join(", ", sorted)); // Infinite loop
该程序没有终止,因为无法完成排序。