You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

滑雪者路径耗时计算C#代码错误排查:重复路径段统计异常

问题分析与修复

问题描述

滑雪者在雪场滑行,移动由'S'(向南10米)、'N'(向北10米)、'W'(向西10米)、'E'(向东10米)的字符序列描述。规则:首次滑行未访问路径段耗时5秒,重复滑行已访问路径段耗时1秒,需计算完成整个路径的总耗时。

输入格式

  • 第一行输入整数t(查询次数)
  • 每行查询为一串仅包含'S'/'N'/'W'/'E'的字符串

用户提交的C#代码

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp8
{
    public class Position
    {
        public int X { get; set; }
        public int Y { get; set; }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            var requestCount = Convert.ToInt16(Console.ReadLine());

            var results = new List<int>();

            for (int i = 0; i < requestCount; i++)
            {
                var movments = Console.ReadLine()
                                      .ToCharArray()
                                      .ToList();

                results.Add(GetMovmentPrice(movments));
            }

            Console.WriteLine(string.Join("\n", results));
        }

        static int GetMovmentPrice(List<char> movments)
        {
            var postion = new Position { X = 0, Y = 0 };
            var knownPositions = new Dictionary<Position, bool>();
            knownPositions.Add(new Position { X = postion.X, Y = postion.Y }, true);

            var price = 0;

            foreach (var movment in movments)
            {
                switch (movment)
                {
                    case 'N':
                        postion.Y++;
                        break;
                    case 'S':
                        postion.Y--;
                        break;
                    case 'W':
                        postion.X--;
                        break;
                    case 'E':
                        postion.X++;
                        break;
                }

                bool founded = false;

                foreach (var key in knownPositions.Keys)
                {
                    if (key.X == postion.X && key.Y == postion.Y)
                    {
                        price += 1;
                        founded = true;
                        break;
                    }
                }

                if (founded)
                    continue;

                price += 5;
                knownPositions.Add(new Position { X = postion.X, Y = postion.Y }, true);

            }
            return price;
        }
    }
}

问题重现

测试输入

5
S
SENNWWEENWESNEEWSWNSNNENSNWSNWSSNNNWSWNNNWNENESNNNSEEEWEWEEWNEWNWSSWENSWSSNNE
EWEENWWWNENENNSSEEWENNWSSWSNSENENWWEEENESNSSESSSEWEESNESSESSSNENSNWNWWWESWEEWEESSSES
SWNEWNESWWSWENENSSNWEWNWNNWNWWEWEWNNSWNEESNSNWSSENWSWSWSNESSSEWESSESWWENWWSWNSWWNEWSNNWE
WNWNSSNWESENEEWSEENENSWWWESSNNESENNENNNWNEWWSEWESSSNSWSENWEESSEESESNWSWSEEWESNSSNS

用户代码输出

5
249
280
276
246

正确输出

5
265
304
300
278

错误原因分析

  1. 核心逻辑错误:代码错误地用「终点位置是否已被访问」来判断路径段是否重复,而题目要求判断的是「当前滑行的路径段(两个相邻位置之间的线段)是否已被滑行过」。即使终点位置已经到达过,只要这段路径是第一次走,就应该加5秒,而不是1秒。
  2. 效率问题:使用Dictionary<Position, bool>存储位置,由于Position是自定义引用类型,默认的相等比较是引用相等而非值相等。虽然代码通过遍历所有键手动比较X/Y值实现了值相等判断,但这种方式效率极低。

修复方案

修复后的代码

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp8
{
    public class Program
    {
        static void Main(string[] args)
        {
            if (!int.TryParse(Console.ReadLine(), out int requestCount))
            {
                return;
            }

            var results = new List<int>();

            for (int i = 0; i < requestCount; i++)
            {
                var movements = Console.ReadLine()?.Trim() ?? string.Empty;
                results.Add(GetMovementCost(movements));
            }

            Console.WriteLine(string.Join("\n", results));
        }

        static int GetMovementCost(string movements)
        {
            int x = 0, y = 0;
            // 存储无向路径段,用ValueTuple存储排序后的两个位置,确保A→B和B→A是同一个键
            var visitedSegments = new HashSet<((int x, int y), (int x, int y))>();
            int totalCost = 0;

            foreach (var move in movements)
            {
                int newX = x, newY = y;
                switch (move)
                {
                    case 'N':
                        newY++;
                        break;
                    case 'S':
                        newY--;
                        break;
                    case 'W':
                        newX--;
                        break;
                    case 'E':
                        newX++;
                        break;
                }

                // 生成路径段的键:将两个位置按坐标从小到大排序,确保无向
                var point1 = (x, y);
                var point2 = (newX, newY);
                var segment = point1.CompareTo(point2) <= 0 ? (point1, point2) : (point2, point1);

                if (visitedSegments.Contains(segment))
                {
                    totalCost += 1;
                }
                else
                {
                    totalCost += 5;
                    visitedSegments.Add(segment);
                }

                // 更新当前位置
                x = newX;
                y = newY;
            }

            return totalCost;
        }
    }

    // 为ValueTuple<int, int>扩展比较方法,用于排序路径段的两个点
    public static class TupleExtensions
    {
        public static int CompareTo(this (int x, int y) a, (int x, int y) b)
        {
            if (a.x != b.x)
                return a.x.CompareTo(b.x);
            return a.y.CompareTo(b.y);
        }
    }
}

修复说明

  1. 路径段存储:使用HashSet<((int x, int y), (int x, int y))>存储已经走过的无向路径段。对于每一步的起点和终点,将两个点按坐标从小到大排序后生成路径段键,确保A→B和B→A被视为同一个键。
  2. 逻辑修正:每次滑行前,先判断当前路径段是否已存在于集合中:
    • 存在:重复滑行,加1秒
    • 不存在:首次滑行,加5秒并将路径段加入集合
  3. 效率优化:使用ValueTuple作为键,避免了自定义引用类型的相等比较问题,同时HashSet的查找是O(1)时间复杂度,比原代码的遍历查找效率高得多。
  4. 输入健壮性:增加了int.TryParse和空输入处理,避免程序因非法输入崩溃。

测试验证

使用提供的测试输入运行修复后的代码,将得到与正确输出一致的结果。

内容的提问来源于stack exchange,提问作者Mar Tin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 23:04:55