بسم الله الرحمن الرحیم
به امید خدا توی این مطلب میخوایم مسئله «آزمون تستی» که در سایت کوئرا هست رو حل کنیم.
حل این مسائل، علاوه براینکه باحاله و سرگرمی :) تمرینی هستن برای افزایش مهارت حل مسئله ما. منم اینجا سعی میکنم بیشتر روش حل مسائل رو توضیح بدم.
برای دیدن مجموعه سوالاتی که از سایت کوئرا حل کردم میتونید این مخزن رو ببینید.
صورت مسئله کامل رو میتونید در سایت کوئرا ببینید. به صورت خلاصه:
یه آزمون با n سوال تستی (۴ گزینهای) برگزار شده، میخوایم سامانهای برای تشخیص نمره افراد بنویسیم. کلید آزمون و پاسخ افراد بهمون داده میشه، ما باید نمره افراد رو محاسبه کنیم و نمایش بدیم.
کلید آزمون از حروف C، B، A یا D تشکیل شده که به ترتیب نشون میده گزینه ۱ یا ۲ یا ۳ یا ۴ صحیح هستن. مثلا اگر توی کلید حرف A بود میدونیم گزینه اول درست بوده و اگر حرف D بود یعنی گزینه ۴ درست بوده. طبیعتا اگر nتا سوال داشته باشیم باید کلید ما هم یه رشته nتایی باشه. مثلا اگر کلید یه آزمون ۳ تایی AAC باشه میدونیم جواب سوال اول و دوم گزینه اول هستش و جواب سوال آخر گزینه ۳.
پاسخبرگ هم یه ماتریس n * ۴ هستش. هر سوال ۴ تا کاراکتر داره که یا # هستش یا O. اگر # بود یعنی اون گزینه علامت خورده و اگر O بود یعنی علامت نخورده.
شرایط درستی یا نادرستی هم به صورت زیر بیان شده:
نمره افراد چطور محاسبه میشه؟ اگر تعداد پاسخهای «درست» یک پاسخبرگ برابر t و تعداد پاسخهای «نادرست» برابر f باشد، نمره این پاسخبرگ برابر است با:
3 × t − f
راهحل کامل #C رو میتونید از این لینک به صورت رنگی(!) ببینید.
ابتدا ورودیهای ساده و اولیه رو میگیریم. یعنی تعداد سوالات، کلید و تعداد پاسخبرگها(تعداد افراد):
var numOfQuestions = Convert.ToInt32(Console.ReadLine()); var keys = Console.ReadLine(); var numOfPersons = Convert.ToInt32(Console.ReadLine());
برای گرفتن ورودی از Console.ReadLine استفاده میکنیم که خروجیش string هستش. برای تبدیل string به int میتونیم از Convert.ToInt32 استفاده کنیم.
حالا باید پاسخبرگها رو از ورودی بگیریم بعد برای هرکدوم نمرش رو حساب کنیم و در خروجی چاپ کنیم.
با توجه به اینکه پاسخبرگها چندتا هستن از یک حلقه استفاده میکنیم.
for (var i = 0; i < numOfPersons; i++) { }
داخل این حلقه باید یه دونه پاسخبرگ بگیریم، بعد نمرش رو حساب کنیم و همینجا چاپ کنیم. (اینجا اشکالی نداره ولی توی پروژههای واقعی هیچوقت قیمهها رو توی ماستها نریزید! و بالعکس! اینجا برای سادگی و اینکه فهم قضیه سخت نشه از این مورد چشم پوشی میکنیم)
هر پاسخبرگ شامل تعدادی رشته هستش. درواقع لیستی از رشتهها هستش به تعداد سوالات. پس برای گرفتن کل پاسخبرگ (لیست رشتهها) لازمه به تعداد سوالات یه حلقه بزنیم و اطلاعات رو توی لیست ذخیره کنیم.
var personAnswers = new List<string>(numOfQuestions); for (var j = 0; j < numOfQuestions; j++) personAnswers.Add(Console.ReadLine());
خب حالا که پاسخبرگ رو داریم کلید هم داریم میتونیم یه متد بنویسیم که این دوتا پارامتر رو بگیره و برامون نمره رو حساب کنه. اسم این متد رو میذاریم CalculateScore. بعدش هم میتونیم نمره رو با Console.WriteLine چاپ کنیم.
var score = CalculateScore(personAnswers, keys); Console.WriteLine(score);
حالا وقتشه برسیم به پیادهسازی متد اصلی. امضای متد ما به این شکل هستش:
private static int CalculateScore(this List<string> personAnswers, string keys) { }
پارامتر اول، پاسخبرگ هستش و پارامتر دومی کلید این آزمون. خروجی هم int هستش بخاطر اینکه نمرات ما صحیح هستن نه اعشاری.
فرض کنید کلید ما AACB هستش و پاسخبرگ یکی از افراد به صورت زیر:
OOOO O#OO OO#O O##O
همونطور که از کلید و پاسخبرگ مشخصه ۴تا سوال داشتیم. جواب سوال اول گزینه اول هستش (که دانشجو علامتی نزده) جواب سوال دوم هم گزینه اول هستش (که دانشجو اشتباه زده) جواب سوال سوم گزینه سوم هستش (که دانشجو درست زده) و چواب سوال چهارم هم گزینه دوم هستش (که دانشجو دوتا گزینه رو انتخاب کرده و طبق صورت سوال، غلط درنظر گرفته میشه)
اگر کلید و پاسخبرگ رو به شما بدن چطوری ارزیابی میکنید؟
خب اینجا هم همینکار رو میکنیم. یه حلقه میزنیم به تعداد حروف کلید و برای اینکه تعداد پاسخهای درست و غلط رو داشته باشیم دوتا متغیر تعریف میکنیم به نام correctAnswers و wrongAnswers:
var correctAnswers = 0; var wrongAnswers = 0; for (var i = 0; i < keys.Length; i++) { }
در داخل این حلقه باید personAnswers[i] رو با keys[i] مقایسه کنیم.
برای هر ردیف لازمه بفهمیم چندتا گزینه علامت خورده.
بیایین همین رو پیادهسازی کنیم.
برای اینکه تعداد گزینههای علامت خورده رو به دست بیاریم میتونیم از متد Count استفاده کنیم.
for (var i = 0; i < keys.Length; i++) { var markedCount = personAnswers[i].Count(c => c == '#'); if (markedCount < 1) continue; if (markedCount > 1) { wrongAnswers++; continue; } }
توی شرطها، اینکه هیچ گزینهای علامت نخورده باشه یا چند گزینه علامت خورده باشه رو بررسی کردیم. حالا در ادامه میدونیم فقط و فقط یک گزینه علامت خورده. باید ببینیم آیا گزینهای که علامت خورده با کلید یکی هست یا نه. اگر یکی بود درست درنظر میگیریم و اگر نه غلط حساب میشه.
توی کلید اگر:
اگر بتونیم یه جوری این تبدیلها (مثلا تبدیل A به ۰) رو انجام بدیم توی یک خط میتونیم شرط رو ارزیابی کنیم. اما چطوری؟
توی کامپیوتر هر حرف کد استانداردی داره. مثلا کد A، عدد ۶۵ هستش. به جدول زیر نگاه کنید:
با توجه به این نکته خیلی راحت میتونیم تبدیل مورد نظر رو انجام بدیم:
if (personAnswers[i][keys[i] - 'A'] != '#') wrongAnswers++; else correctAnswers++;
مثلا اگر حرف کلید ما B باشه، میاد کد A (که ۶۵ هست) رو از کد B (که ۶۶ هست) کم میکنه و خروجی میشه ۱. اینطوری چک میکنه که آیا خونه ۱ علامت خورده یا نه. اگر علامت خورده باشه یعنی جواب درسته وگرنه غلطه.
در پایان هم با توجه به اینکه تعداد جوابهای درست و غلط رو داریم میتونیم نمره فرد رو محاسبه کنیم:
private static int CalculateScore(this IReadOnlyList<string> personAnswers, string keys) { var correctAnswers = 0; var wrongAnswers = 0; for (var i = 0; i < keys.Length; i++) { var markedCount = personAnswers[i].Count(c => c == '#'); if (markedCount < 1) continue; if (markedCount > 1) { wrongAnswers++; continue; } if (personAnswers[i][keys[i] - 'A'] != '#') wrongAnswers++; else correctAnswers++; } return (3 * correctAnswers) - wrongAnswers; }
میتونید کد کامل رو از این لینک ببینید.
امیدوارم براتون مفید بوده باشه. :)
انشاءالله در پناه خدا همیشه صبور، شاد و موفق باشین.