Wednesday, December 26, 2012

DUNNO1 - бодлогуудын анализ

А:

Энэ тодорхой ойлгомжтой дасгал байсан, алдах хүн байхгүй байлгүй.



int main() {
string a;
cin >> a;
for (int i = 0; i < (int)a.size(); ++i) {
if (string("CAMBRIDGE").find(a[i]) == string::npos) {
cout << a[i];
}
}
cout << endl;
}


B:

Эхлээд мэдэгдэж байгаа дүнгүүдээ буурахаар эрэмбэлье. Одоо дээрээс нь эхлэн оролцогч бүр дээр түрүүлэх боломжтой эсэхийг нь шалгая, К дугаар оролцогч гэж үзье. Энэ оролцогч түрүүлэхийн тулд бусад оролцогчидод хэдэн оноо өгөх вэ л гэсэн бодлого юм. Дараах Greedy санааг ашиглана. К дугаар оролцогчоо түрүүлүүлэхийн тулд шууд N оноог өгөх нь ойлгомжтой. Буурахаар эрэмбэлсэн учир К-н доор байгаа хүмүүс бидэнд огт хамаагүй, тэд нарт хэдэн ч оноо нэмээд К дугаар оролцогчийн дээр гарч чадахгүй. Харин К-н дээр байранд байгаа хүмүүсийг К-н дээр байлгахгүй тулд аль болох хамгийн бага оноог өгөх ёстой. Дахиад сайн бодъё, К-н дээр байгаа хамгийн өндөр оноотой тамирчинд 1 оноог өгнө, хамгийн өндөр оноотой 2 дахь тамирчинд 2 оноо гэх мэтчилэн К-1 дахь тамирчинд К-1 оноо өгнө. Тэгвэл уг К дугаар тамирчин түрүүлэх зайлшгүй бөгөөд хүрэлцээтэй нөхцөл нь:

 a[K] + N ≥ max{ a[1] + 1, a[2] + 2, …, a[K - 1] + K - 1 }, энд а[] массивт оролцогчидын оноог буурахаар эрэмбэлсэн байгаа.

Одоо гол асуудал нь К-н өмнөх оролцогчидын хамгийн их оноотойг яаж олох вэ? Үүнийг жижигхээн DP ашиглаад шийдчиж болно, үүнийг уншигч таньд нээлттэй үлдээе.


Бодолт: O(NlogN)




#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;

int a[300000];

bool cmp(int x, int y) { return x > y; }

int main () {
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d", a+i);
sort(a, a+n, cmp);
int s = 0;
int maX = 0;
for (int i = 0; i < n; ++i) {
s += (a[i] + n >= maX);
maX = max(maX, a[i] + i + 1);
}
printf("%d\n", s);
system("pause");
return 0;
}




C:

Өгөгдсөн N тооны length(N) = (N, K, ... ) гэсэн тоонуудаас бүтсэн байг. Хэрвээ сайн бодож үзсэн бол яг К буюу өөрийг нь хуваадаггүй хамгийн бага тоо, хэдий N<=10^17 ч маш бага тоо байгаа. Миний бодлогоны хувьд 50-аас хэтрэхгүй, гэхдээ Хонгор 41 гэж аваад давуулсан байна лээ, хангалттайлдаа уг нь кк (энийг яаж шалгах вэ?? бодож үзээрэй). Одоо тэгвэл бодлого маань ерөөсөө ийм, тэр К гэдэг тоо [A ; B] завсар дахь хэдэн тооны length()-т яг тэр байгаа байрлалдаа орох вэ? Тийм тоонууд маань бүгд Length(N) = Length(K)+1 нь ойлгомжтой.
К-г багтаасан тоонуудыг яаж тоолох вэ?
[A ; B] завсарт орших өөрийг нь хуваадаггүй хамгийн бага тоог К гэе (энд яг дээрхтэй адилхан К тоог ярьж байгаа шүү), тэгвэл К-аас бага бүх тоонууд тухайг тоог хуваана гэсэн үг. Энэ нь LCM - Хамгийн бага ерөнхий хуваагдагч, LCM(1, 2, 3, ..., K-1) мөн тухайн тоог хуваана гэсэн үг. Нэмэлт шаардлага бол тухайн тоо К-д хуваагдахгүй байг ёстой. Энэ хоёр нөхцөлийг хангах тоонуудыг тоолоод L гэе. Тэгвэл бодлогын хариу Нийлбэр{ (length(K)+1)*L } энд K = 2..41 байхад хангалттай.
Одоо тэдгээр тоонуудыг яаж тоолох талаар хальт санаа өгье. [A;B] завсар дахь тоонуудаас LCM(1, 2, 3, .., K-1)-т хуваагддаг тоонуудыг тоолсон гэж үзье. Эдгээр тоонуудаас К-д хуваагддагийг хасна, мөн огтлол буюу К болон LCM(1, 2, 3, ..., K-1)-т хуваагддагийг хасах ёстой, энэ чинь юу гэсэн үг вэ гэхээр LCM(1, 2, 3, ..., K)-г олоод хасчина гэсэн үг. Ерөнхийдөө бол А болон В-д харьяалагдах D-д хуваагддаг тоог тоолох ба дараах томёо хүчинтэй: B / D - (A - 1) / D, Үүнийг яагаад гэдэгийг өөрсдөө оролдож хараарай, уг нь энгийн л зүйллдээ.





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

int main(){
long long A, B, ret = 0;
cin >> A >> B;

// LCM = lcm(1, 2, ..., K-1)
long long LCM = 1;

int length[42];
length[2] = 1;

for (int K = 2; K < 42; K++){

for (int i = 2; i < K; i++) {
if (K % i != 0) {
length[K] = length[i] + 1;
break;
}
}

long long tempLCM = LCM;
vector<int> lcmNumbers;
for (int p = 2, k = K; k > 1; p++) {
if (k % p == 0) {
lcmNumbers.push_back(p);
while (k % p == 0) {
k /= p;
}
}
}
if ((int)lcmNumbers.size() == 1) {
tempLCM *= lcmNumbers[0];
}

ret += (length[K] + 1) * (B/LCM - (A-1)/LCM);

if (tempLCM > B || tempLCM < 0) break;
ret -= (length[K] + 1) * (B/tempLCM - (A-1)/tempLCM);

LCM = tempLCM;
}
cout << ret << endl;
}




D болон E::

Энэ хоёрын бодолт бол хоюулаа тодорхой шууд л Bruteforce хийгээд тоол, харийн өөрсдийн чинь код бичих чадвараас л шалтгаалж хүнд хөнгөн нь тодорхойлогдоно.



D бодолт:



    int n, m;
cin >> n >> m;
int a[n];
for(int i=0; i<n; i++) cin >> a[i];
int sol = 0;
for(int i=0; i<n; i++)
for(int j=i+1; j<n; j++)
for(int k=j+1; k<n; k++)
if( a[i]+a[j]+a[k] <= m )
sol = max( sol, a[i]+a[j]+a[k] );
cout << sol << endl;
return 0;




E бодолт:






char a[505][505];
int sol[5];

int main()
{
int m, n; scanf("%d%d", &m, &n);
for(int i=0; i<5*m+1; i++) scanf("%s", a[i]);
for(int i=0; i<m; i++)
for(int j=0; j<n; j++)
{
int x = 1 + 5*i;
int y = 1 + 5*j;
int k = 0;
for(int l=0; l<4; l++)
k += (a[x+l][y] == '*');
sol[k]++;
}
for(int i=0; i<5; i++)
printf("%d%c", sol[i], i==4?'\n':' ');
}




F:



Энэ бодлогыг бодлогонд Stack эсвэл рекурс өгөгдөлийн бүтэц мөн BitMask 2-г ашиглана. Эхлээд өгөгдсөн тэмдэгт мөртөө нэвтрэлт хийгээд бүх зөв хаалтуудыг Стек-даа хийгээд ав, байрлалаар нь, тэгээд битмаскаа ашиглаад бүх боломжоор нь хариугаа гаргаж аваад эрэмблэхэд бодогдоно. Кодоо цэвэрхэн бичихгүй бол TLE авахуулах тестүүд их байгаа.



#include <iostream>
#include <string>
#include <set>
#include <vector>
#include <stack>

#define FORC(it, C) for(__typeof((C).begin()) it = (C).begin(); it != (C).end(); ++it)

int main(void) {
std::string ss;
std::cin >> ss;
int n = ss.size();
std::vector< int > pos(n, -1);
std::stack< int > current;
int numOfBar = 0;

for (int i = 0; i < n; ++i) {
if (ss[i] == '(') {
pos[i] = numOfBar++;
current.push(pos[i]);
}
if (ss[i] == ')') {
pos[i] = current.top();
current.pop();
}
}

std::set< std::string > ret;

for (int i = 1; i < (1<<numOfBar); ++i) {
std::string temp ;
for (int j = 0; j < n; ++j) {
if ((ss[j] != '(' && ss[j] != ')') || !(i & (1 << pos[j]))) {
temp += ss[j];
}
}
ret.insert(temp );
}

for (auto &retOut: ret) {
std::cout << retOut << std::endl;
}

return 0;
}




Дараагийн хоёр бодлого буюу G, H хоёр дээр дахин шинэ пост зориулна. Бас С бодлого дээр би өөр маш сонирхолтой бодолт хийсэн байгаа. Тэрийгээ давхар сонирхуулнаа.

  Код үлдээх уг нь муу, гэхдээ яахав сурах нэг нь сурах юм байвал сураад, сурахгүй нэг нь шууд хуулаад илгээнэ биз, надад хамаагүй асуудал.

Tuesday, December 25, 2012

Блогийн нээлт бас тэмцээн

Сайн байцгаана уу

Хүмүүс блог, хувийн сайт нээнгүүтээ нээлтээ хийдэг байхаа. Миний хувьд тэгж бодоогүй, сураггүй нээх шууд нээнгүүтээ тиймэрхүү зүйл бичих тохиромжгүй санагдаж байсан юм. Хэрвээ санаж байгаа бол энэ блогийн хамгийн эхний пост some day гэсэн үг байгаа. Цаг нь болсон юмшиг байна хэхэ.

Анх 2011 оны 12-р сарын 7-нд блогоо нээж байсан юм. Блог нээх хэд хэдэн шалтгаан байсан. Би өөрийгөө зүгээр л энгийн дундаж чадвартай хүн гэж үздэг учраас, юм сурах зовлонг мэднэ. Бүр сурах гээд хүсээд байхад эх хэл дээр минь материал, заах хүн байхгүй байхын зовлонг бүр ч мэднэ. За тэгээд байсан ч ойлгомжгүй болих тайлбарласан бас буруу орчуулсан материал, ном үзэж их цаг бардаг байлаа. Дээрээс нь тухайн алгоритм онолыг ойлгосны дараа надад хүн ингээд хэлээд өгцөн бол ойлгочих байж гэж бодогддог байлаа. Энэ зүйл ядаж бага биш ч хүмүүст тохиолддог зүйл гэж бодож байна. Би өөрөө бол тийм ч сайн бодлого боддоггүй, гэхдээ энэ сурсан бага ч гэсэн зүйлээ шинэ соргого дээр нь хойч үедээ мөн сурах гэж хичээж, зовж байгаа бүх хүмүүст эх хэл дээр нь төвөггүй уншаад ойлгох зүйл үлдээе гэж байнга хүсдэг маань энэ блогийн хамгийн том оршил юмшүү. Тиймээс бичсэн зүйлээ ойлгомжтой энгийн ямар ч тэнэг хүн уншаад ойлгомжтой байх тал дээр үнэхээр их цаг гаргадаг. Гэхдээ үр дүнтэй байгаа эсэхээ одоо хүртэл мэдэхгүй байна, уг нь блогийн хандалт маш их байгаа, даанч сэтгэгдэл үлдээж, санал гомдол бичдэг хүн байхгүй болохоор тэр. За олон юм нуршихаа болъё, ямар ч байсан блогийхаа нээлтийг хийж байгаадаа баяртай байна хэхэ.
www.spoj.pl/DUNNO Энэ сайт дээр блогтоо бичсэн алгоритмтай холбоотой бодлого, хичээлүүдийн бодлого тавигдах болно.
-----
Тэмцээн:
Энэ удаагийн тэмцээнд нийт 15-н оролцогч оролцлоо. Дажгүй асуудалгүй сайхан л болох шиг болсон. Дүн дараах байдалтай байна.



Эхний 3-н байр Хонгор, Сугардорж багш мөн Золжаргал 3т баяр хүргэе.
Энэ удаагийн тэмцээний хувьд ерөнхийдөө 10-н жилийхнийг хэр байгааг харая гэж бодож байсан, нээх 10-н жилийхэн орсонгүй гэхдээ орсон хэдийн хувьд бэлтгэлээ сайн хийгээрэй гэж хэлмээр санагдсан. Бодолтуудыг дараагийн постоор оруулна.
Энэ постоо өндөрлөхөөсөө өмнө энэ чиглэлээр сонирхож явах хүүхдүүд мөн хэрэгтэй хүмүүүст нь өөрийн зүгээс зөвлөгөө хэлмээр байна.

 Олимпиад бодож битгий бодлого бод, зүгээр л бодлогондоо анхаар. Ямар нэгэн алгоритм бодлого бодож байгаа бол өөрийгөө тухайн зүйлдээ бие даагаад судалгаа шинжилгээний ажил хийж байна гэж үзээд хэдэн ч цагаар хамаагүй суу. Нэмээд хэлэхэд юм хийхэд хэр их цаг зарцуулж суусандаа биш хэр үр дүнтэй сууснаар чинь чиний мэдэх зүйл тодорхойлогдоно. Бодлого бодох нэрээр ком дээр сууж ФБ нээж тэтрис тоглох, охидуудтай чатлахыг хэлэхгүй шүү хэхэ, дэмий сууж нуруугаа ядраасан нэг цагаас бодлого, алгоритмандаа анхаарч төвлөрч чадсан 10 минут ч илүү байх. Мөн ямар нэгэн бодлого бодсоныхаа дараа энэ бодлогоос юуг сурч авав гэдэгээ байнга эргэж хар. Ганц хоёр бодлого бодчоод битгий бардаж хөөрөөрэй, миний ажигласнаар хүүхдүүд хэдэн бодлогоны ард гарчаад их зан гаргаад багш нараа ч тоохгүй өөр зүйл хийдэг, энэ бол хамгийн буруу зүйл. Өөр хоёр нэмэх зүйл бол Математик мөн Англи хэл. Энэ хоёрыг ер нь алгоритм эхлэхээсээ өмнө 1 жил зарцуулаад ороход буруудах юм байхгүй дээ. Гэхдээ хүн хүн өөр, сурах арга барил ондоо, юм ойлгох чадвар ялгаатай байдагаас хамаараад яг 1 жил ч шаардахгүй байх. Зүгээр онолын ч гэх юмуу, сонирхсон зүйлээ миний иймэрхүү блог эсвэл хүмүүсээс асуугаад мэдчиж болно, гэхдээ бүр гүнзгийрэх гэвэл яах аргагүй математик мэдлэг сэтгэлгээ мөн хангалттай материал байхгүйгээс гадны ном сайт унших хэрэг гарна тэгэхээр англи хэл зайлшгүй болж байгаа юм. Хэл муу, мат сэтгэлгээ мэдлэг тааруугаасаа болж маш их цаг алдаж байна. За өөр ч болох шивдээ, нэг зүйлийг нэмж хэлэхэд хүмүүсээ хүнд баярлалаа гэж хэлж сураарай, би энэ блогийг нээгээд мөн цөөнгүй тэмцээнийг зохиогоод өдий хүртэл баярлалаа гэдэг үгийг маш цөөхөн сонссон, тэмцээн зохиох бэлтгэл ажил бодлого зохиох тестлэх гээд ямар их цаг авдаг гээч.

За түр баяртай.

Saturday, December 15, 2012

Warm up

Тэмцээн эхэлтэл, мѳн тэмцээнийхаа шалгагчтай танилцангаа дараах бодлогыг бодож байна уу.

http://www.spoj.com/DUNNO/problems/TUGS/

Ер нь бол http://www.spoj.com/DUNNO/ энэ саит дээр би бодлогууд тавьж байх болноо. Бодлоготой холбоотой санал хүсэлт, асуух зүйлээ коммэнт үлдээж асууж болно шүү.

UPDATE

Спож асуудалтай байгаа тул тэмцээнээ 3-н хоногоор хойшлууллаа. Уучлаарай хүмүүсээ. Монголын цагаар 25-ны орой 8-д болно, 5-н цагийн хугацаатай. Өөр улс оронд байгаа хүмүүс эндээс цагаа харна уу. Болох газар хуучнаараа байх байхаа. За асуудал гаргасанд уучлаарай.