یه چیز پیشنهاد بدین بذارم اینجا

نوشته های من در مورد برنامه نویسی، غیر از برنامه نویسی و چیز‌های دیگه...

یه چیز پیشنهاد بدین بذارم اینجا

نوشته های من در مورد برنامه نویسی، غیر از برنامه نویسی و چیز‌های دیگه...

آنالیز مسابقه ۱۰۶ سایت Codeforces

جمعه, ۲۱ بهمن ۱۳۹۰، ۱۰:۲۷ ب.ظ

امروز تو این کانتست شرکت کردم. از عملکرد خودم توش نسبتا راضی بودم. سه تا سوال اول رو با سرعت نسبتا خوبی زدم. سوال چهارم رو یک کم اول ترسیدم ازش رفتم سوال پنجم رو هم خوندم. یکم این قضیه باعث شد که وقت هدر بره. این اتفاق که سره انتخاب سوال گیج می‌شم برام کمابیش می‌افته که باید سعی کنم کمتر پیش بیاد. سوال چهار ایده جدیدی نداشت و تقریبا حل کردنش طول نکشید. و کدش رو هم با سرعت خوبی زدم. سوال پنج stringای بود. باز هم مثل همیشه بلد نبودن KMP کار دستم داد. البته تونستم به راه دیگه پیدا کنم که به زور تایم نشه. فعلا که WA شده. خود راه حلم باید درست باشه. هروقت باگش رو پیدا کردم آنالیز اون رو هم می‌نویسم. به قول حامد (صالح) خاک تو سرم! اگه سوال پنج رو گرفته بودم سوم می‌شدم. حالا مهم نیست. یه انگیزه دیگه ایجاد شد که من برم KMP یاد بگیرم. که می‌دونم آخرش یاد نمی‌گیرم!!

برای دسترسی به سوال ها و ارسال پاسخ به آدرس مقابل بروید: http://www.codeforces.com/contest/149

سوال یکم ـ ‌Business Trip

به شما عدد صحیح و نامنفی k و دنباله‌ای شامل ۱۲ عدد داده می‌شود. شما باید بگویید حداقل چند تا از اعداد دنباله را باید انتخاب کرد تا مجموع اعداد انتخاب شده از k کمتر نباشد و در صورتی که این کار غیر ممکن است این را اعلام کنید.

راه حل

می‌توانید اعداد را از بزرگ به کوچک مرتب کنید و از بزرگترین اعداد تا جایی که مجموع بزرگتر مساوی k شود بردارید. اگر همه اعداد را برداشتید و مجموع اعداد از k‌ کمتر بود این کار غیر ممکن است.

کد

#include <iostream>
#include <algorithm>

using namespace std;

int main() {
	int a[12], k;

	cin >> k;
	for (int i = 0; i < 12; i++)
		cin >> a[i];

	sort(a, a+12, greater<int> ());

	int sum = 0, pos = 0;
	while (pos < 12 && sum < k) {
		sum += a[pos];
		pos++;
	}

	if (sum < k)
		cout << -1 << endl;
	else
		cout << pos << endl;

	return 0;
}

سوال دوم ـ Martian Clock

نمایشی از ساعت دیجیتال به فرم Hour:Minute به شما داده می‌شود. اعداد ساعت و دقیقه لزوما در مبنای ده‌دهی نوشته نشده اند. شما باید مشخص کنید ساعت داده شده در چه مبناهایی معتبر است. می‌دانیم که ساعات روز از ۰ تا ۲۳ اند و دقایق هر ساعت از ۰ تا ۵۹. اگر ساعت داده شده در تعداد بی‌شماری از مبناها معتبر است این را اعلام کنید.

راه حل

اگر یک ساعت در تعداد بی‌شماری از مبناها معتبر باشد به این معنا است که هرچه مبنای ساعت از حد خاصی بزرگتر شود عدد نوشته شده ارزشش بیشتر نمی‌شود. پس می‌توان نتیجه گرفت عدد ورودی یک رقمی است. اگر عددی بیش از یک رقم داشته باشد حتما در مبنای ۱۰۰ مقدارش از ۵۹ و ۲۳ بزرگتر خواهد شد. پس کافیست به ازای هر مبنا بین ۲ تا ۱۰۰ چک کنیم آیا ساعت داده شده در آن مبنا معتبر است یا خیر. طبق نتایج گرفته شده اگر ساعتی در مبنای ۱۰۰ معتبر باشد در تمام مبناهای بالاتر معتبر است.

برای این که بفهمیم یک ساعت در مبنای b معتبر است یا نه کافیست چک کنیم ارقامش بین ۰ تا b-۱ باشند و مقدار ساعتی که در آن مبنا نشان می‌دهد بین ۰ تا ۲۳ باشد. همچنین مقدار دقیقه بین ۰ تا ۵۹ باشد.

کد

#include <iostream>
#include <string>
#include <sstream>
#include <vector>

using namespace std;

const long long INF = 1e9;

// Converts a digit character to its value
int c2i(char c) {
	if (c <= '9')
		return c-'0';
	return c-'A'+10;
}

// Converts a number from base "base" to its value
// If "num" is not a valid base "base" number returns infinity
long long conv(string num, int base) {
	long long res = 0;
	for (int i = 0; i < (int)num.length(); i++) {
		if (c2i(num[i]) >= base) return INF;
		res = res*base + c2i(num[i]);
	}
	return res;
}

int main() {
	string inp, h, m;
	cin >> inp;
	for (int i = 0; i < (int)inp.length(); i++)
		if (inp[i] == ':')
			inp[i] = ' ';
	istringstream sin(inp);
	sin >> h >> m;

	vector<int> res;
	for (int base = 2; base <= 100; base++)
		if (conv(h, base) < 24 && conv(m, base) < 60)
			res.push_back(base);

	if (res.empty())
		cout << 0 << endl;
	else if (res.back() == 100)
		cout << -1 << endl;
	else {
		for (int i = 0; i < (int)res.size(); i++)
			cout << res[i] << " ";
		cout << endl;
	}
	return 0;
}

سوال سوم ـ Division into Teams

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

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

تضمین شده است که چنین افرازی برای افراد همواره وجود دارد. همچنین می‌دانیم تعداد افراد از ۱۰۰،۰۰۰ بیشتر نیست.

راه حل

یک روش تقسیم این‌گونه است که ابتدا افراد را بر حسب مهارتشان مرتب کنیم (مثلا از کوچک به بزرگ). سپس یکی درمیان در تیم‌ها بگذاریمشان. یعنی افرادی که در جایگاه فرد قرار دارند در یک تیم و افرادی که در جایگاه زوج قرار دارند در تیم دیگر.

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

فرض کنید افراد را بر حسب مهارت به صورت دنباله a۱, a۲, ..., an مرتب کرده ایم و فرد an قویترین فرد ممکن است. با فرض این که n فرد است می‌خواهیم ثابت کنیم:

(a۱+a۳+...+an)-(a۲+a۳+...+an-۱) ≤ an

از آنجا که می‌دانیم:

(a۱-a۲)+(a۳-a۴)+...+(an-۲-an-۱) ≤ ۰

می‌توانیم به دو طرف نامساوی an را اضافه کنیم تا به عبارت اول برسیم.

حال اگر n‌ زوج باشد می‌توانیم an را حذف کنیم و تیم ها را افراز کنیم. سپس an را اضافه کنیم و با توجه به این که an-۱ ≤ an نابرابری را ثابت کنیم.

کد

#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>

using namespace std;

int main() {
	vector<pair<int, int> > V;
	int n;

	cin >> n;
	for (int i = 0; i < n; i++) {
		int p;
		cin >> p;
		V.push_back(make_pair(p, i+1));
	}

	sort(V.begin(), V.end());

	cout << (n+1)/2 << endl;
	for (int i = 0; i < n; i += 2)
		cout << V[i].second << " ";
	cout << endl;

	cout << n/2 << endl;
	for (int i = 1; i < n; i += 2)
		cout << V[i].second << " ";
	cout << endl;

	return 0;
}

سوال چهارم ـ Coloring Brackets

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

  • هر پرانتز با رنگ نشده باشد یا قرمز یا آبی باشد.
  • هیچ دو کاراکتر مجاوری در رشته در صورتی که هر دو شان رنگ شده‌اند همرنگ نباشند.
  • از بین هر پرانتز و جفتش دقیقا یکی رنگ شده باشد.

یک عبارت معتبر از پرانتزها به شما داده می‌شود. تعداد روش‌های رنگ آمیزی آن را حساب کنید. چون ممکن است جواب بسیار بزرگ شود باقی‌مانده آن را بر ۱۰۰۰۰۰۰۰۰۷ چاپ کنید.

راه حل

تابع داینامیک f(a, b, ca, cb) را در نظر بگیرید. مقدار f برابر با تعداد راه‌هایی است که رشته از کاراکتر a تا b را طوری رنگ کنیم که رنگ کاراکتر a برابر با ca و رنگ کارکتر b برابر با cb باشد. در کل سه نوع رنگ داریم. رنگ ۰ به معنای بی‌رنگ است، رنگ ۱ قرمز و ۲ آبی. در تابع f فرض می‌کنیم b‌ جفت a است. برای حساب کردن مقدار f تمام جفت پرانتز هایی را که بلافاصله در عمق ۱ از ab آمده اند را در نظر می‌گیریم. یعنی جفت های مشکی در زیر:

a-> ((...)(...)...(...)) <- b

حال که رنگ a و b‌ را می‌دانیم تعداد رنگ آمیزی‌های پرانتزهای داخل ab را می‌توان با مقادیر تابع f برای جفت پرانتزهای مشکی شده توسط یک داینامیک نسبتا ساده حساب کرد.

کد

در کدی که در ادامه آمده است برای هر جفت ab یک ماتریس ۳×۳ تولید کرده ام به طوری که درایه سطر ca و ستون cb آن برابر با f(a, b, ca, cb) است. تابع merge نیز دو ماتریس را می‌گیرد و تعداد راه های کنار هم گذاشتن آن‌ها را حساب می‌کند. اگر طول رشته ورودی n باشد. پیچیدگی زمانی کد برابر با O(n) است.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define mat vector<vector<ll> >
typedef long long ll;

mat empty = mat (3, vector<ll> (3, 0ll));
mat one = mat (3, vector<ll> (3, 0ll));

const ll MOD = 1000000007;
string s;
int head;

mat merge(mat a, mat b) {
	mat c = empty;

	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			for (int k = 0; k < 3; k++)
				for (int l = 0; l < 3; l++)
					 if (j == 0 || k == 0 || (j != k))
						 c[i][l] += a[i][j] * b[k][l], c[i][l] %= MOD;
	return c;
}

mat process() {
	head++;
	if (s[head] == ')') {
		head++;
		return one;
	}

	vector<mat> V;

	while(s[head] != ')')
		V.push_back(process());
	head++;

	for (int i = 1; i < (int)V.size(); i++)
		V[0] = merge(V[0], V[i]);

	mat res = empty;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			if ((i == 0 || j == 0) && (i != 0 || j != 0))
				for (int k = 0; k < 3; k++)
					for (int l = 0; l < 3; l++) {
						if (i && k == i) continue;
						if (j && l == j) continue;
						res[i][j] += V[0][k][l];
						res[i][j] %= MOD;
					}

	return res;
}

int main() {
	for (int i = 1; i <= 2; i++) one[i][0] = one[0][i] = 1;
	cin >> s;

	vector<mat> V;

	while (head < (int)s.length())
		V.push_back(process());

	for (int i = 1; i < (int)V.size(); i++)
		V[0] = merge(V[0], V[i]);

	mat res = V[0];
	ll tot = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			tot += res[i][j];
	tot %= MOD;

	cout << tot << endl;

	return 0;
}

سوال پنجم ـ‌ Martian Strings

رشته s1s2...sn را به شما داده اند. رشته sa...b+sc...d را به ازای هر چهار عدد ۱ ≤ a ≤ b < c ≤ d ≤ n را «خوب» می‌نامیم. به شما m رشته داده می‌شود طول هرکدام از این رشته ها از ۱۰۰۰ بیشتر نیست. همچنین m از ۱۰۰ و n از ۱۰۰،۰۰۰ بیشتر نیست. شما باید بگویید چند تا از این m رشته خوب هستند.

راه حل

<در اینجا قرار می‌گیرد>

کد

<در اینجا قرار می‌گیرد>

  • جمعه, ۲۱ بهمن ۱۳۹۰، ۱۰:۲۷ ب.ظ
  • سید حامد ولی زاده

نظرات  (۱۶)

 :D
یس! اولین نظر!!!
ممممم
یه چیزی! خیلی کار خوبیه این راه حل گذاشتن! مرسی! آدم می تونه راحت باهاشون پیش بره! (مخصوصا ترجمه ی فارسیم داره :))‌ ) دستتون درد نکنه به هر حال!
خسته نباشین! مرسی که وبلاگو از کپک و کپکو از وبلاگ در اوردین D:

 "که می دونم آخرش یاد نمی گیرم" خیلی قوی بود :))) لایک! (من مطمئنم عین این جمله رو قبلا یه جا شنیدم :-" )
!!!!conteste 106 codeforces bud
!!!na 116
D:
پاسخ:
yesss!!
درست می‌کنم!
مرسی از خبر. :)
آفرین. 
این kmp چیه ؟؟؟؟
پاسخ:
یه الگوریتمه واسه پیدا کردن همه پیدایش‌های یک رشته به طول m توی یک رشته به طول n در پیچیدگی زمانی O(m+n)
 به به سلام حامد جون .
چه خبرا  !!!!!(اوه باید اینجا علامت سوال باشه ؟؟؟؟؟؟؟؟؟؟)

kmp فک کنمknuth-morris-path algorithm باشه دی !!! (مطمءنم نیست ولی اگه خواستی ضایمون بکنی اروم اروم بکن زیاد ضایع نشم.)
پاسخ:
سلام!
خوبی؟
نه بابا! همینه! :دی
منتظر بودم حامد ضایعت کنه که نکرد!
P = Pratt

پاسخ:
:)))
الان خودم ضایه شدم.
مرسی!
 سلام!
پاسخ نظرات خصوصی می دونید کجا ارسال می شه؟
اینو پرسیده بودم:
از چه روشی به جز kmp حل می شه؟
ممنون!
پاسخ:
سلام.
منم حسش نبود میل بدم!
این سوال با همون روش احمقانه پیدا کردن رشته هم اکسپت می‌شه.
من با تابع strstr اکسپت شدم تا اونجا که یادم میاد.
تست های سوال خیلی ضعیفه!
  • رضا به بالایی
  • سلام!
    فکر شما راجع به هک اشتباهه ! شما فکر می کنی اگه هک نمی شدی ، اکسپت می شدی؟
    به احتمال خیلی زیادی آخر مسابقه رانگ می خوردی!!!
    پس همون (!)362 میشدی!
    پاسخ:
    ممنون از توضیح :)
     شاید!

    حالا چرا لو می دی مردمو؟!

    خوبه منم هویت یکی دیگه (مثلا ژنیک رو لو بدم! (البته خیلی جا ها لو رفته!!!))
     :))))))
    به من چیکار دارین آخه؟! :))) یکی دیگه هویت یکی دیگه رو فاش میکنه ژنیکو تهدید میکنن :))
     
    همه که می دونن ژنیک کیه دیگه! نیازی به لو دادن نیست!
    دلت خوشه ها...

    ژنیک :دیگه هیچکس دم دست نبود آخه ... :)))

    راستی آقای ولیزاده اگه وقت دارین تا قبل از این که کانتست امشب CodeForces تموم شه سوالاش رو ترجمه کنید ..

    مرسی!
    سلام حامد جون!!!!

    اومدم بپرسم به نظرت سوال گزاره ها تو مرحله 1 ، 4 میشه یا 3 ؟؟؟ 
    پاسخ:
    سلام پویا جون!
    انقدر سر این سوال بحث شد که دیشب خوابشو دیدم. واقعا!
    تو خوابم که گفته بود می‌شه ۳ تا. در مورد ۴ بحثی نکرده بود.
    به نظر خودم هم می‌شه ۳ تا. :)
     آفرین حامد جوووووووووووووووووووووووون!!!!!

    من هم می گم 3 تا میشه نه چهار تا!! :)
    هر آدم عاقلی با یک ذره فکر کردن می فهمه 3 میشه. نمی فهمم چرا این قدر سر این سوال بحث شده. آزمون HM 2012 هفته‌ی پیش در آمریکا برگزار شد. این آزمون جمعه در www.combinatorics.vcp.ir هم برگزار میشه. اونایی که می خوان امتحان بدن برن به آدرس بالا. اونایی که نمی دونن HM چیه نترسن. یه آزمون ساده پاسخ کوتاهه و برای مرحله 1 و مرحله 2 تستی خوبه
    @amin
    نه عزیزم .همون کانتست 106 بود.من خودم الن دارم virtual practice میکنم.
    ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
    شما میتوانید از این تگهای html استفاده کنید:
    <b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
    تجدید کد امنیتی