مدیریت زمان نیما


  • محدودیت زمان: ۱ ثانیه
  • محدودیت حافظه: ۲۵۶ مگابایت

نیما که یک آدم بسیار پرکار است، همزمان هم درس می‌خواند و هم سر کار می‌رود. حالا او برای اینکه ساعت‌هایش را مدیریت کند نیاز به کمک شما دارد.

شبانه‌روز او مثل همه‌ی ما ۲۴ ساعت دارد، از این ۲۴ ساعت، WW ساعت را مشغول کارهای شرکت است و SS ساعت را مشغول درس‌های دانشگاه است و در نهایت هم RR ساعت برای استراحت دارد.

اما همچنان محاسبات ما درست در نمی‌آید. چرا که وقت خالی او بیشتر از چیزی که محاسبه می‌کنیم می‌شود. بنابراین فرض می‌کنیم او در II ساعت روز دو کار را با هم انجام می‌دهد. یعنی هم مشغول درس خواندن است و هم مشغول کار مفید!

حالا از شما می‌خواهیم حساب کنید که نهایتا چند ساعت از روز را استراحت می‌کند؟ (RR) را به دست آورید.

ورودی🔗

ورودی تنها شامل یک خط است که در آن سه عدد حسابی WW و SS و II با فاصله از هم آمده اند. 0W,S,I24 0 \le W, S, I \le 24

همچنین می‌دانیم که مجموع WW و SS از شبانه‌روز بیشتر نیست.

W+S24 W + S \le 24

و البته مقدار II از هر یک از درس و کار کمتر یا مساوی است.

IS,W I \le S, W

خروجی🔗

در خروجی فقط یک عدد RR را چاپ کنید که نشان دهنده‌ی تعداد ساعت‌های خالی نیما در روز است.

مثال🔗

ورودی نمونه ۱🔗

10 11 2
Plain text

خروجی نمونه ۱🔗

5
Plain text

در این مثال نیما ۱۰ ساعت مشغول کار‌های دانشگاه و ۱۱ ساعت مشغول کارهای شرکت بوده است. ۲ ساعت از روز را نیز مشغول هر دو کار بوده است.

پس می‌توان گفت ۱۹ ساعت جمعا برای دانشگاه و شرکت وقت گذاشته و ۵ ساعت هم وقت آزاد دارد.

ورودی نمونه ۲🔗

4 5 4
Plain text

خروجی نمونه ۲🔗

19
Plain text

در این مثال نیما ۴ ساعت برای دانشگاه کار کرده و ۵ ساعت برای شرکت، حالا هر ۴ ساعت درس خواندن همزمان با کارهای شرکت بوده است پس در کل ۵ ساعت وقت گذاشته است و ۱۹ ساعت زمان خالی داشته.

کتاب‌خانه‌ی پردردسر


برای مدیریت بهتر کتاب‌هایمان به یک کتاب‌خانه نیاز داریم. این کتاب‌خانه مقداری ظرفیت دارد که بیشتر از آن نمی‌تواند کتاب در خود جا دهد.

این کتاب‌خانه مشخصات زیر را دارد:

  • از هر کتاب دقیقاً یکی دارد که یا موجود است و یا به یک نفر امانت داده شده است.
  • اسم کتاب‌ها به بزرگی و کوچکی حساس نیست، مثلاً Golestan و gOlEsTan دقیقاً یک کتاب هستند.
  • یک نفر می‌تواند چند کتاب را امانت بگیرد و از این نظر محدودیتی نیست.
  • در صورتی که یک کتاب امانت گرفته شده باشد، جایش همچنان محفوظ است و ظرفیت آن برای اضافه شدن کتاب دیگری باز نمی‌شود.
  • پیام‌های خطای زیر از متد‌های مختلف کتاب‌خانه دریافت می‌شود. در صورتی که هیچ خطایی نباشد نیز OK داده می‌شود:
The book has not been borrowed
The book is already borrowed by <borrower name>
The book is already in the library
The book is not defined in the library
Not enough capacity
OK
Plain text

تشخیص این‌که کدام ارور در چه زمان(ها)ی باید برگردانده شود بر عهده‌ی شماست.

کتاب‌خانه

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به صورت زیر است:

library/
├── go.mod
├── go.sum
├── main.go
└── main_sample_test.go
Plain text

در فایل main.go چند تابع و متد وجود دارد که شما باید آن‌ها را کامل کنید.

تابع NewLibrary🔗

این تابع یک اشاره‌گر به یک شی از نوع کتاب‌خانه برمی‌گرداند. در این تابع باید یک شی جدید بسازید و ساختار داخلی آن را مطابق آن‌چه خودتان تعریف کرده‌اید مقداردهی کنید.

extensionFromNamemain.go
type Library struct {
}

func NewLibrary(capacity int) *Library {
    // TODO
    return nil
}
Go

متد AddBook🔗

در این متد شما اسم یک کتاب را گرفته و در صورتی که شرایط برقرار بود (مثلاً کتابخانه جا داشت و این کتاب قبلا در کتابخانه نبود) آن را اضافه می‌کنید و OK بر می‌گردانید. در غیر این صورت بسته به مورد ارور مناسب را در قالب یک رشته برمی‌گردانید.

extensionFromNamemain.go
func (library *Library) AddBook(name string) string {
    // TODO
    return ""
}
Go

*راهنمایی: در صورتی که کتاب‌خانه پر باشد و کتابی با نام تکراری وارد شود، خطای مربوط به کتاب تکراری (و نه پر بودن ظرفیت) باید برگردانده شود.*

متد BorrowBook و ReturnBook🔗

در این دو متد یک نفر یک کتاب را امانت می‌گیرد و سپس آن را پس می‌دهد.

در مواردی نیز ممکن است خطا پیش آید مثلا کتابی که امانت گرفته شده را دوباره کسی بخواهد امانت بگیرد یا کسی کتابی که در حال حاضر امانت گرفته نشده را بخواهد پس دهد.

extensionFromNamemain.go
func (library *Library) BorrowBook(bookName, personName string) string {
    // TODO
    return ""
}

func (library *Library) ReturnBook(bookName string) string {
    // TODO
    return ""
}
Go

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی توابع خواسته شده، فایل main.go را آپلود کنید.

طولانی بود، نخوندم


حتما خودتان بهتر می‌دانید که ما برنامه‌نویسان فراموشکار، جزئیات و آرگومان‌های دستورات را به راحتی یادمان می‌رود. برای این منظور خیلی اوقات صفحات manual دستورها یا همان man page ها را چک می‌کنیم. برای مثال با وارد کردن دستور man ls می‌توانیم مستندات رسمی ابزار ls را در صفحه‌ی دوست داشتنی ترمینالمان بخوانیم.

متاسفانه به جز فراموشکار، گاهی کم‌حوصله هم هستیم و دنبال کردن یک مستند رسمی طولانی می‌تواند جانفرسا باشد. مثلا man bash که توضیحات پوسته‌ی bash را به ما نشان می‌دهد بسیار طولانی است و حدود پنج هزار خط است! برای حل این مشکل، مستندات غیر رسمی‌ای تحت نام «طولانی بود نخوندم» یا "too long didn't read» یا به طور مخفف TLDR توسط کاربران ایجاد شد که نسخه‌ی خلاصه‌ شده و کاربردی از مستندات است و با مثال‌ها و توضیحات و ... می‌تواند بسیار در زمان صرفه‌جویی کند. برای آشنایی اولیه با این مستندات می‌توانید از این لینک استفاده کنید.

اما استفاده از سایت شاید برای همه‌ی کاربران گزینه‌ی اول نباشد و بسیاری از ما دوست داریم یک برنامه‌ی سمت کاربر داشته باشیم که به این شکل از آن استفاده کنیم:‌ tldr bash و خودش از صفحات آنلاین خوانده و اطلاعات را در اختیار ما بگذارد. البته تنها مشکل ما این نیست. به جز این مورد، با توجه به سرعت محدود اینترنت، ما دوست داریم با استفاده از حافظه‌ی نهان (کش) در زمان و مصرف اینترنت نیز صرفه‌جویی کند، به این ترتیب صفحاتی که تا کنون بازدید شده‌اند را در خود ذخیره کند و در زمان استفاده، صفحه‌ی ذخیره شده را بازگرداند.

کلاینت اصلی این نرم‌افزار با زبان Javascript و در محیط Node.js توسعه داده شده اما کلاینت‌های دیگری نیز برای آن وجود دارد که می‌توانید لیستی از آن‌ها را اینجا ملاحظه کنید. توصیه می‌شود یکی از آن‌ها را نصب کنید و با آن کار کنید.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به صورت زیر است:

.
├── go.mod
├── go.sum
├── tldr.go
├── db.go
├── main.go
└── main_sample_test.go
Plain text

در فایل main.go چند تابع و متد وجود دارد که شما باید آن‌ها را کامل کنید.

آن‌چه باید پیاده‌سازی کنید🔗

در فایل tldr.go‍ یک اینترفیس وجود دارد که باید آن را پیاده‌سازی کنید. این اینترفیس دو متد دارد که در ادامه توضیح داده می‌شود.

extensionFromNametldr.go
type TLDRProvider interface {
    Retrieve(string) string
    List() []string
}
Go

در فایل main.go، یک استراکت وجود دارد که قرار است اینترفیس گفته شده را پیاده‌سازی کند. همچنین یک تابع سازنده داریم

extensionFromNamemain.go
type TLDRDBCached struct {
}

func (t *TLDRDBCached) Retrieve(key string) string {
}

func (t *TLDRDBCached) List() []string {
}

func NewTLDRDBCached(nonCachedProvider TLDRProvider) TLDRProvider {
    return nil
}
Go

متد Retrieve🔗

در متد Retrieve، یک کلید که اسم همان ابزاری است که توضیحات بیشترش را می‌خواهیم به ما داده می‌شود و انتظار می‌رود توضیح و راهنمای کار با آن را به شکل یک رشته برگردانیم.

زمانی که یک کلید را دریافت می‌کنیم دو حالت دارد. حالت اول حالتی است که قبلا آن را دیده‌ایم و در کش ذخیره کرده‌ایم، پس باید به سرعت و بدون معطلی از کش آن را خوانده و برگردانیم. حالت دوم حالتی است که تا به حال این کلید را ندیده‌ایم، در این حالت باید با کمک منبع بیرونی یا همان provider (که خودش مثلا اینترنت داده‌ها را می‌خواند) و هم در کش ذخیره کند و هم برگرداند.

متد List🔗

این متد باید لیست تمام مطالبی که در provider وجود دارند را برگرداند. به بیان دیگر این لیست یک رشته برمی‌گرداند شامل اسم تمام ابزارهایی که در tldr داکیومنت مربوط به آن وجود دارد. برای دسترسی به این لیست به سادگی می‌توانید متد List را در تامین‌‌کننده فراخوانی کنید.

تنها نکته‌ای که وجود دارد این است که ترتیب این لیست باید تا حدی عوض شود. تفاوت به این صورت است که بین تمام مواردی که در لیست قرار می‌گیرند، در لیست خروجی شما باید اول اسم ابزارهایی که صفحه‌ی آن‌ها در کش موجود است آورده شود و در ادامه باقی ابزارها بیایند. مثلا اگر ابزار zsh تا کنون فراخوانی شده و در کش موجود است، اول این لیست zsh وجود داشته باشد و باقی ابزارها مثل bash قرار گیرند. ترتیب اعضایی که در کش هستند اهمیتی ندارد و ترتیب اعضایی که در کش نیستند نیز اهمیتی ندارد.

سازنده‌ی NewTLDRDBCached🔗

این سازنده یک شی تامین کننده از جنس همین اینترفیس می‌گیرد و یک شی از نوع TLDRDBCached که به درستی مقداردهی کرده است برمیگرداند.

روش ذخیره‌سازی در حافظه‌ی نهان🔗

از آنجا که به فصل گرم سال نزدیک می‌شویم و ممکن است برق سیستم برود، نیاز داریم که این اطلاعات در یک پایگاه‌داده ذخیره شوند تا با خاموش و روشن شدن سیستم، همچنان دسترسی به اطلاعات از طریق دیتابیس موجود باشد. برای این منظور یک DBMS از نوع postgres تدارک دیده‌ایم که می‌توانید از آن استفاده کنید. همچنین یک رابطه شی-ارتباط به نام Gorm هم برای شما تدارک دیده شده که می توانید برای سهولت بیشتر از آن استفاده کنید.

در فایل db.go یک تابع به نام GetConnection وجود دارد که یک شی از نوع *gorm.DB می‌دهد و می‌توانید با آن به دیتابیس و امکانات Gorm دسترسی داشته باشید. در سیستم خودتان می‌توانید اطلاعات اتصال به دیتابیس را مطابق سیستم خودتان بروز کنید ولی آن فایل را آپلود نخواهید کرد و سمت سرور فایل db.go‍ مطابق کانفیگ سمت سرور موجود است.

در فایل tldr.go‍ که قبلا بخشی از آن را دیده‌ایم، یک موجودیت نیز برای شما آورده شده است به نام TLDREntity:

extensionFromNametldr.go
type TLDREntity struct {
    gorm.Model
    Key string `gorm:"primaryKey;size:100"`
    Val string `gorm:"size:1000"`
}
Go

شما برای حل این سوال باید اطلاعات خود را در قالب شی‌ای از این نوع در دیتابیس ذخیره کنید و حق تغییر در این شی را نیز ندارید.

پس دقت کنید که فقط اجازه دارید کش کردن داده را با استفاده از Gorm انجام دهید.

توجه کنید که در تست‌ها بررسی می‌شود که جدول متناظر با این شی به طور مناسب پر شده باشد.

تضمین‌ها🔗

  • تضمین می‌شود به متد Retrieve هیچگاه کلیدی که سمت سرور وجود ندارد پاس داده نمی‌شود.
  • تضمین می‌شود که اسم ابزار همواره زیر ۱۰۰ کارکتر طول دارد و تماما از کارکترهای انگلیسی تشکیل شده است.
  • تضمین می‌شود که متن توضیح ابزار همواره زیر ۱۰۰۰ کارکتر طول دارد و تماما از کارکترهای انگلیسی تشکیل شده است.
  • تضمین می‌شود که قبل از اجرای هر تست دیتابیس خالی است و فقط یک جدول خالی متناظر با TLDREntity وجود دارد.
  • تضمین می‌شود زمان اجرای تست‌ها، فراخوانی GetConnection یک پوینتر به یک شی معتبر از نوع gorm.DBخواهد بود و با آن به شکل مناسب به دیتابیس دسترسی خواهید داشت.
  • تضمین می‌شود در provider حداقل داکیومنت مربوط به یک ابزار وجود دارد.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی توابع خواسته شده، فایل main.go را آپلود کنید.

تابلو اعلانات


سال 1410 است و برای همه دانشگاه‌ها ابلاغیه آمده که باید تابلوهای اعلانات خود را الکترونیکی کنند. در بازار برنامه‌های مختلفی برای کار با تابلوهای الکترونیکی وجود دارد ولی دانشگاه شما میخواهد از برنامه متفاوتی استفاده کند و شما را به عنوان برنامه نویس آن انتخاب کرده است.

نمونه‌ای از تابلو اعلانات موجود رو میتونید اینجا ببینید

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به صورت زیر است:

.
├── go.mod
├── go.sum
├── main.go
└── main_sample_test.go
Plain text

در فایل main.go چند تابع و متد وجود دارد که شما باید آن‌ها را کامل کنید.

از شما خواسته شده که نحوه اضافه کردن اعلامیه به تابلو و برداشتن آن از روی تابلو شبیه تابلوهای فیزیکی موجود باشد تا شکل و شمایل آشنای خود را حفظ کند. به این معنی که ممکن است اعلامیه‌ها روی هم قرار بگیرند و اگر بخواهیم اعلامیه زیرین را از روی تابلو حذف کنیم، باید ابتدا اعلامیه‌های روی آن برای لحظه‌ای برداشته شوند و سپس دوباره سر جایشان گذاشته شوند. همچنین تمام اعلانات یک عدد منحفصر به فرد (ID) دارند.

آن‌چه باید پیاده‌سازی کنید🔗

در وهله اول به شما اینترفیس تابلو و اعلانات روی آن داده شده است. (به توضیحات راجع به این توابع بعدا پرداخته خواهد شد)

extensionFromNamemain.go
type AnnouncementBoard interface {
    getAnnouncementIDsAt(int, int) []int
}

type AnnouncementPaper interface {
    addTo(AnnouncementBoard, int, int) error
    removeAndGetIDsOnTop() []int
}
Go

همچنین نیاز است برای تابلو و اعلانات توابعی با امضاهای زیر تعریف کنید که به عنوان تابع سازنده (constructor) عمل میکنند. تضمین میشود در تست‌ها از خروجی همین توابع استفاده خواهد شد.

extensionFromNamemain.go
func NewBoard(row int, col int) AnnouncementBoard {

}

func NewPaper(width int, height int, ID int) AnnouncementPaper {

}
Go

تابع NewBoard: این تابع تعداد ردیف‌ها و تعداد ستون‌های تابلو را به عنوان ورودی میگیرد و یک شی از نوع AnnouncementBoard برمیگرداند.

تابع NewPaper: این تابع عرض (تعداد ستون‌هایی که روی تابلو اشغال میکند) و طول (تعداد ردیف‌هایی که روی تابلو اشغال میکند) و آیدی اعلان را به عنوان ورودی میگیرد و یک شی از نوع AnnouncementPaper برمیگرداند. تضمین میشود که عرض تمام اعلانات عددی فرد است.

متدهای اینترفیس‌ها🔗

تابع getAnnouncementIDsAt: این تابع به ترتیب شماره یک ردیف و شماره یک ستون از تابلو را میگیرد و آیدی اعلانات موجود در این نقطه را به ترتیب از زیرترین اعلان به روترین آن برمیگرداند.

تابع addTo: اعلامیه را به تابلویی که به عنوان ورودی میگیرد میچسباند. رو مقدار بعدی در ورودی به ترتیب ردیف و ستونی است که پونز در آن قرار خواهد گرفت. (ردیف و ستون اینجا از 0 شمرده میشوند. مثلا اگر در تابع NewBoard تعداد ردیف را 5 داده باشیم، اینجا اگر بخواهیم پونز روی ردیف اول باشد عددش را 0 میدهیم و اگر بخواهیم روی ردیف آخر باشد عددش را 4 میدهیم.) موقعیت پونز روی کاغذ ستون وسط و بالاترین ردیف است. یعنی اگر عرض کاغذ 3 و طول آن 4 باشد، یعنی کاغذ 3 ستون و 4 ردیف از تابلو را اشغال کند، پونز روی ردیف اول (از بالا) و ستون دوم قرار میگیرد. (یادآوری: تضمین میشود عرض کاغذ عددی فرد است)

همچنین در این تابع باید چک کنید که نه پونز و نه خود کاغذ از تابلو بیرون نزنند و اگر زدند یک error برگردانید (پیام درون ارور مهم نیست)

و دقت کنید که در تابع getAnnouncementIDsAt فقط پونزهایی که در آن نقطه قرار دارند را نمیخواهیم. هر قسمتی از هر کاغذی در آن نقطه از تابلو قرار داشته باشند باید آیدیش در لیست خروجی آن تابع در جای درستش باشد.

همچنین تضمین میشود هر شی که از تابع NewPaper برگردانده میشود، در هر لحظه فقط و فقط به یک تابلو وصل خواهد شد. البته میتوان از روی یک تابلو آن را برداشت و سپس روی تابلوی دیگری نصب کرد.

تابع removeAndGetIDsOnTop: وقتی این تابع روی یک اعلان صدا میشود، آن اعلان و تمام اثراتش باید از روی تابلویی که در حال حاضر رویش قرار دارد حذف شود. همچنین باید این تابع لیستی از اعلاناتی که رویا این اعلان قرار دارند را برگرداند (همان داستان که میخواهیم مثل تابلوی فیزیکی اول اعلانات رویش را برداریم تا به ایت اعلان برسیم و بعد آن رویی‌ها را سر جایشان برگردانیم).

البته این تابع یک چالش دارد که اگر پیاده سازیش نکنید فقط بخشی از نمره اسن قسمت را میگیرید. آن هم این است که این اعلانات باید به ترتیب از روترین اعلان به زیرترین مرتب شود. برای اعلاناتی که باید برداشته شوند ولی به هم ارجعیتی ندارند (هیچکدام روی دیگری نیست)، مهم نیست با چه ترتیبی در خروجی قرار میگیرند.

برای مثال اگر یک اعلان با آیدی 200 را بخواهیم حذف کنیم، در حالی که یک اعلان با آیدی 150 روی گوشه راست بالای آن قرار دارد و اعلان با آیدی 250 روی گوشه سمت چپ آن، در حالی که 150 و 250 با هم هیچ هم پوشانی ندارند، مهم نیست خروجی [150 250] است یا [250 150]. ولی اگر 200 را بخواهیم حذف کنیم در حالیکه 150 روی گوشه راست بالای 200 قرار داشته باشد و 250 گوشه راست بالای 150، خروجی باید باشد [150 250]، یعنی خانه 0 آن 250 و خانه 1 آن 150 باشد.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی توابع خواسته شده، فایل main.go را آپلود کنید.

پیپ


پیپ یا pip یکی از بازیگران مهم دنیای پایتون است. این نرم‌افزار که سابقا نامش pyinstall بود الان به نام pip تغییر نام داده است. این نام همانند GNU یک مخفف بازگشتی است. در واقعا pip مخفف Pip Install Paackages است. اطلاعات بیشتر در صفحه‌ی ویکیپدیای پیپ در دسترس است.

از شما می‌خواهیم در ادامه، این پکیج‌منیجر دوست داشتنی را با زبان گو و برای زبان گو پیاده‌سازی کنید. پس بدون معطلی فایل‌های اولیه‌ی پروژه را از این لینک دانلود کنید.

آن‌چه باید پیاده‌سازی کنید🔗

برای این سوال از شما می‌خواهیم به شکل Test Driven Development عمل کنید. در واقع آن‌چه باید پیاده‌سازی کنید، هرچیزی است که باعث پاس شدن تست‌های نمونه که در اختیار شماست می‌‌شود. برای این سوال تست‌های اصلی دقیقا همان تست‌های نمونه هستند و به این ترتیب شما می‌توانید با خواندن دقیق تست‌ها متوجه جزئیات سوال شوید و کد خودتان را همراه با پیاده‌سازی تست کنید.

راهنمایی‌ و تضمین‌ها🔗

  • تضمین می‌شود دقیقا همین تستی که در اختیار شماست در کوئرا اجرا می‌شود.
  • می‌توانید برای راحتی کار از کتاب‌خانه‌ی این لینک و این لینک استفاده کنید. اما استفاده از هیچ کتابخانه‌ی دیگری مجاز نیست.
  • برای اینکه از بین تست‌های فیل شونده بتوانید توسعه را جلو ببرید، ابزار اجرای تست خود را طوری تنظیم کنید که با اولین فیل شدن تست، اجرا را متوقف کند.
  • با دقت در پکیج‌ها و ایمپورت‌ها، انتخاب کنید که باید توسعه را از کدام پکیج شروع کنید.
  • سعی کنید در قدم اول یک نسخه‌ی قابل کامپایل پیاده کنید و بعدا به سراغ تکمیل متدها بروید.
  • اشکالی ندارد اگر همه‌ی تست‌ها پاس نمی‌شود، هر مقدار از تست‌ها را که می‌توانید پاس کنید، می‌توانید نمره همان را بگیرید. فقط برنامه لازم است کامپایل شود.
  • برای اجرای تست‌ها می‌توانید از دستور زیر شروع کنید و بسته به نیاز آن را تغییر دهید:
    go test "./..." -count=1 -v
    Bash

آن‌چه باید آپلود کنید🔗

  • یک فایل زیپ آپلود کنید که وقتی آن را باز می‌کنیم دقیقا پوشه های fs و commands را بببینیم. داخل این پوشه‌ها فایل‌های go را قرار دهید.