Monday, July 7, 2014

C++, STL - II.

Map-аас вектор үүсгэх:




map<string, int> M; 
// ...
vector< pair<string, int> > V(all(M)); // umnuh post deer all(c)-g (c).begin(), (c).end() gesen macro zarlasan bga.


Вектор сортлогдсон байгаа. Ингэх нь мап хэрэггүй болсон мөн элементүүдийг индексжүүлэх хэрэгтэй үед ашиглах нь тохиромжтой. Нэг зүйлийг дурьдахад Мап-г индекслэх ашиглаж байгаа ч энэ нь хайх үйлдэл цаана нь ажиллаж байгаа ба Лог хугацаа зарцуулна.

Олонлогийн үйлдэлүүдийг агуулагч бүтэц төрлүүдэд хэрэглэх. Агуулагч бүтэц төрөл гэж би шугаман болон мод бүтэцтэй бүх төрлүүдийг хэлж байгаан шүү.
- 'union' нэгтгэх, R = A+B
- Огтлолын элементүүд, R = A*B
- 2 олонлогт 2ууланд нь зэрэг байдаггүй элементүүд, R = A*(~B) or R = A-B
- симметрик ялгаа, R = A XOR B

Дээрх 4-н үйлдэлд зориулагдсан функцууд бүгд байдаг. set_union(...), set_intersection(...), set_difference(...) and set_symmetric_difference(...).

Жишээ болгон огтлолыг яаж олохыг харууъя. Гэснээс бүгд яаж ашиглах нь адилхан шүү.

int data1[] = { 1, 2, 5, 6, 8, 9, 10 }; 
int data2[] = { 0, 2, 3, 4, 7, 8, 10 };

vector<int> v1(data1);
vector<int> v2(data2);

vector<int> tmp(max(v1.size(), v2.size());

vector<int> res = vector<int> (tmp.begin(), set_intersection(all(v1), all(v2), tmp.begin());



accumulate:
Хэдэн параметрай дуудахаас шалтгаалаад өөр өөр үйлдэл хийдэг. Жишээ нь тухайн векторт байгаа элементүүдийн нийлбэрийг олъё.
3 дахь параметр нь анхны утга байх ёстой ба мөн уг утгын төрлөөс хамаарч ямар төрлөөр утгаа буцаахаа шийднэ.

vector<int> v; 
// ...
int sum = accumulate(all(v), 0);


vector<int> v; 
// ...
long long sum = accumulate(all(v), (long long)0);



Үржвэрийг нь олохдоо 4 дахь параметр нэмэх ба дараах байдлаар бичнэ. Анхны утгаа 1-ээр өгөхөө мартав.

vector<int> v; 
// ...
double product = accumulate(all(v), double(1), multiplies<double>());


ЧУХАЛ

Энэ хүртэл зааж ашигласан жишээ бүгд энгийн төрлүүд ашигласан байсан, бүхэл тоон төрөл гэх мэт. Тэгвэл бид өөрсдөө бүтэц төрөл мөн класс байгуулвал эдгээр төрлүүдийг жишээ нь эрэмбэлэх хэрэг гарна. Үүнийг яаж зааж өгөх вэ. С++ бол зөвхөн энгийн төрлүүд байвал яаж эрэмблэх вэ гэдэгээ л мэднэ. Үүнийг зохицуулахын тулд бид тухайн класс бүтэцдээ зөвхөн "<" тэмдэгт таарвал яаж вэ гэдэгийг нь зааж өгөхөд хангалттай. Жишээ болгон хүртвэл хуваарьтэй бутархай тоон төрөл байгуулж үүнийгээ векторт хийж хэрхэн эрэмбэлэхийг харуулъя.

struct fraction { 
int n, d; // (n/d)
// ...
bool operator < (const fraction& f) const {
if(false) {
return (double(n)/d) < (double(f.n)/f.d);
}
else {
return n*f.d < f.n*d;
}
}
};

// ...

vector<fraction> v;

// ...

sort(all(v));


Одоо зарим нэгэн чухал алгоритмуудыг хэрхэн хялбар бичиж болохыг харуулъя.

DFS.

typedef vector<int> vi;
typedef vector<vi> vvi;


int N; // Oroin too.
vvi W; // graph
vi V; // tuhain oroid nevtersen eseh.

void dfs(int i) {
if(!V[i]) {
V[i] = true;
for_each(all(W[i]), dfs);
}
}

bool check_graph_connected_dfs() {
int start_vertex = 0;
V = vi(N, false);
dfs(start_vertex);
return (find(all(V), 0) == V.end());
}



BFS, queue.
/*
чиглэлгүй граф, мөн хөршийн матриц ашигласан жишээ.
*/

int N; // Оройн тоо.
vvi W; // хөрш оройнууд


bool check_graph_connected_bfs() {
int start_vertex = 0;
vi V(N, false);
queue<int> Q;
Q.push(start_vertex);
V[start_vertex] = true;
while(!Q.empty()) {
int i = Q.front();
Q.pop();
tr(W[i], it) {
if(!V[*it]) {
V[*it] = true;
Q.push(*it);
}
}
}
return (find(all(V), 0) == V.end());
}



Dijkstra, priority_queue.

typedef pair<int,int> ii;
typedef vector<ii> vii;
typedef vector<vii> vvii;
vi D(N, 987654321);

priority_queue<ii,vector<ii>, greater<ii> > Q;
D[0] = 0;
Q.push(ii(0,0));

while(!Q.empty()) {

ii top = Q.top();
Q.pop();

int v = top.second, d = top.first;
if(d <= D[v]) {
tr(G[v], it) {
int v2 = it->first, cost = it->second;
if(D[v2] > D[v] + cost) {
D[v2] = D[v] + cost;
Q.push(ii(D[v2], v2));

}
}
}
}


Dijkstra, set.
  vi D(N, 987654321);
set<ii> Q;
D[0] = 0;
Q.insert(ii(0,0));

while(!Q.empty()) {
ii top = *Q.begin();
Q.erase(Q.begin());
int v = top.second, d = top.first;


tr(G[v], it) {
int v2 = it->first, cost = it->second;
if(D[v2] > D[v] + cost) {
if(D[v2] != 987654321) {
Q.erase(Q.find(ii(D[v2],v2)));
}
D[v2] = D[v] + cost;
Q.insert(ii(D[v2], v2));
}
}
}

C++, STL.

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

Энэ удаагийхаа постоор C++ -н ерөнхий мэдлэгээ дээшлүүлэх, боломжит функцууд мөн уг хэлэнд аль хэдийнээ бичигдцэн байдаг өгөгдөлийн бүтэцүүдийг хэрхэн ашиглах талаар бичье. Маш амархан энгийн зүйлүүд байгаа.



Юуны өмнө заагчийн талаар маш товч тодорхой тайлбарлая.

Ямар ч хамаагүй төрлийн хувьсагч байг. Энэ хувьсагч нь тухайн төрлийн нэг утгыг хадгалдаг, бид бүгдээрэй үүнийг мэднэ. Тэгвэл тухайн энэ хадгалагдаж байгаа утга маань компьютерт санах ой гэх төхөөрөмжинд, энэ төхөөрөмж нь бидний мэдэх РАМ, хадгалагддаг. РАМ нь тодорхой хэмжээний утгуудыг хадгална, эдгээрийгээ 0-ээс эхлээд дугаарлая. Тэгэхээр програмд зарласан болон хэрэглэгдэж байгаа бүх өгөгдөл, утгууд санах ойд хадгалагдах ба мөн санах ойд хаана хадгалагдаж байгаагаас хамаараад бүгд санах ойн дугаартай байх нь. ЗААГЧ бол яг энэ дугаарыг л авч явдаг нэгэн төрлийн хувьсагч юм.
Анхаарах зүйл нь заагч нь тухайн зааж байгаа хаяганд байрлах утгийн төрөлтэй яг адилхан байх ёстой.
Заагч төрөл зарлахдаа тухайн заагчийн төрөлийг тавиад од тэгээд хувьсагчийн нэр.

int *intZaagch; //int turuliin zaagch.

char *charZaagch; //char turuliin zaagch.



Ямар нэгэн энгийн төрлийн хувьсагч байг. Уг утгын санах ойн дугаарт хандахдаа тухайн хувьсагийн нэрний өмнө & тавьж хандна, харин ямар нэгэн заагч хувьсагийн зааж байгаа хаяг дахь утганд хандахдаа од тавиад заагийн нэрийг тавьдаг, дараах жишээг харна уу.



int a = 20; //engiin huvisagch.

int *intZaagch; //zaagch huvisagch.

intZaagch = &a; //intZaagch a-n zaagch bolj bn.



cout << a << " " << &a << " " << intZaagch << " " << *intZaagch << endl;



*intZaagch = 10;

cout << a; // end 10-g hevlene. Yagaad gevel *intZaagch = 10 ni tuhain sanah oid bga utgiig 10 bolgoj bga ba ene ni a-n utga bsn.






Iterator:



Итератор бол заагч. Обьект хандалтад програмчлалд тодорхой ганцхан зүйлээс бүтсэн төрөл гэж байдаггүй. Бүх зүйл ямар нэгэн өөрсдийн гэсэн хэд хэдэн төрөл зүйл болон туслах функцуудтэй зүйлүүд нийлж нэг обьект үүсгэдэг. Олон ийм обьектуудтай массив байг. Бид энэ массивын бүх обьектоор гүйж боловсруулалт хийх гэж байгаа гэж бодъё. Гэвч бид ямар төрөл юм гэдэгийг мэдэхгүй. Иймээс итератор хэрэглэж шаардлага гарна, зөвхөн санах ойд хаана байгаагаар нь гүйгээд хаягаар нь дамжиж орно. Ерөнхий ойлголт авсан байх гэж үзээд түр хойш нь тавиад жишээгээр сайн хараад авна уу.


Дараагийн заах зүйл Макро директив ашиглах. Макро директив нь

#define orlogch orluulagch

гэсэн форматтай байдаг ба үүнийг ухаалаг ашигласнаар програмын код маш бага болдог. Мөн бас нэг давуу тал нь компайлер тухайн код-г компайлдахаас өмнө Preprocessing гэсэн алхам байдаг ба энд хийгддэг зүйлүүдийн нэг нь код-г шалгаж orlogch таарах юм бол orluulagch-г өөрөөр нь шууд сольж тавьдаг. Энэ нь компайлдах, оптимиз хийхэд шууд нөлөөлдөггүй тул үр дүн сайтай хурдан ажилладаг програм үүсэхэд тустай байдаг. Жишээ нь бид тогтмол утга const double INF = 1e10;. Энд const double гэсэн 2 түлхүүр үгнүүд компайлдах явцад өөр зүйл хийлгэдэг. Энэ удаагийн пост компайлер, оптимиз хэрхэн ажилдаг талаар биш тул үүнээс цааш тайлбарлах дэмий байх. Товчхондоо бол дээрх бичиглэлээс мактор дирэктив ашигласан нь хамаагүй хурдан шүү. #define INF 1e10




STL-рүү орохоос өмнө дахиад нэг зүйлийг анхааруулах үүднээс хэлье. Бид ихэнх тохиолдолд аргументтэй функц бичдэг. Энэ нь тухайн функц дуудагдаж ашиглагдахад тухайн аргументуудыг яг дахин шинээр хуулбарлан санах ойд үүсгэж байж функц ажилладаг, энэ хуулбарлаж шинээр үүсгэх нь мэдээж хэрэг шугаман хугацаа зарцуулна. Энэ үнэхээр хэрэгтэй юу? Үүнээс зайлхийх арга нь заагчуудыг функцийн аргумантад ашиглах хэрэгтэй. Жишээ болгон дараах код-н хэсгийг харна уу.


void function(int a[], int size) {...//end shineer sanah oid uusgene.


void function(int &a[], int &size) {...//end shuud zaagchtai haritsah tul yamar ch iluu uildel hiihgui.


STL - Standart Template Library.


Хэрэглээ маш өндөртэй өгөгдлийн бүтэцүүд бас бусад функц алгоритмуудыг хэрэглэхэд бэлэн болгоод маш өндөр түвшинд, алдаагүй шахуу, биччихсэн байдаг сан юм. Ихэнх, бараг бүгд, STL-н функцүүдын аргументууд нь хаанаас хаа хүртэл гүйхийг нь зааж өгөхийг шаарддаг.


Шугаман массив бүтэцтэй өгөгдөлийн бүтцүүд. Хамгийн энгийн зүйл бол массив. int a[101]; Гэвч массивын сул талууд бол, бид хэмжээг нь заавал байж болох хамгийн томоор нь зааж байж зарладаг. Энэ нь дахиад л санах ойд бидэнд хэрэгтэйгээс хамаагүй илүү зай эзлэж цаг үрдэг. Мөн бидэнд дараах үйлдлүүд массив дээр хийх хэрэгтэй байг.

Массивт элемент нэмэх.

Элемент хасах.

Ямар нэгэн элемент байгаа эсэхийг шалгах.

Массиваас бүгд ялгаатай байх бүх элементийг олох.. гэх үйлдлүүд хэрэгтэй байг. Мэдээж бид үүнийг өөрсдөө бичиж чадна, энэ нь жоохон төвөгтэй мөн тухайн массив маань ямар төрөл вэ гэдэгээс шалтгаалаад өөр өөр бичих хэрэг гарна. Тэгвэл үүнийг бүгдийг нь шийдчихсэн STL-н сан байна. Энэ нь vector юм. Постоо товч бөгөөд тодорхой байлгахын тулд жишээ кодууд маш их ашиглана шүү.



Шинээр вектор үүсгэх:



#include < vector >

using namespace std;

int main() {

vector < int > v; //hooson vector.

/*

shuud v[0] = 1; ingevel ajillah uyiin aldaa garna RE. Yagad gevel ene ni odoohondoo hooson vector.

*/

v.resize(10); // odoo harin 10 urttai vector bolloo.

vector < int > vv(10); // buh element ni teg baih 10 urttai vector.

vector < int > vv(10, -1); //buh element ni -1 baih 10 urttai vector.

vector < int > vvv(vv.begin(), vv.end()); //vv.begin() ni vv-n hamgiin ehnii elementiig zaana.

//vv.end() ni hamgiin suuliihiig zaana. End yag tsaanaa ng iterator guij baigaa ba hamgiin ehnii bolon hamgiin suuliin

//elementiing hoorond guigeed vvv-ruu hiij bn.

vector < int > vi(vvv); //shuud vvv-g huulj shine vector uusgej bn.

vector < vector < int > > matrix(10, vector < int > (10, 0)); //2d.


int size = vi.size(); // vector-n hemjeeg butsaana. Gehdee ene size() function O(1) bish shuu!

//vector-g hooson bnuu gdgiig shalgahdaa empty() function true butsaahna uu gdgeer shalgaj bgaarai.

for (size_t i = 0; i < size; i++) { v.push_back(i); } //engiin cycle-aar guij bn. End mun vector-t herhen element
nemj baigaag harna uu.

for (vector < int > ::iterator it = vi.begin(); it != vi.end(); it++) { printf("%d ", *it);} //iterator ashiglan guij bn.

}




Pair:


utility санд байдаг дараах байдлаар тодорхойлогдсон 2 элементээс бүтсэн өгөгдлийн бүтэц.

template < typename T1, typename T2 > struct pair {

T1 first;

T2 second;

};



Т1, Т2 нь дурын төрөл тул юу ч байж болно.

#include < utility >

using namespace std;

int main() {

pair < string, pair < int, int > > P;

P = make_pair("aa", make_pair(1, 2)); // make_pair gj shineer uusgej bolno.

pair < int, int > point(1, 1); //mun ingej uusgej bolno,

cout << P.first << " " << P.second.first << " " << P.second.second;// herhen elementuuded handaj bgag harna uu.

return 0;}




Reverse: - тухайн вектор эсвэл массив-г хамгийн сүүлийхийг хамгийн урд гэх мэтээр байрыг солино.

#include < algorithm >

#include < vector >

using namespace std;

int main() {

vector < int > v;

const int N = 10;

int a[10];

for (int i = 0; i < N; i++) {

v.push_back(i);

a[i] = i;

}

reverse(v.begin(), v.end());

reverse(a, a+n);

return 0;}



Хайлтын функцууд:



Аргументууд нь яг адилхан, өөр нэг төстэй зүйлүүд нь хэрэв тухайн хайж байгаа зүйл олдохгүй бол тухайн хайлт хийгдэж байгаа агуулагчийн төгсгөлийн заагчийг буцаадаг.



if (find(vector.begin(), vector.end(), HaihObject) != vector.end()) {..



int data[5] = { 1, 5, 2, 4, 3 };

vector < int > X(data, data+5);

int v1 = *max_element(X.begin(), X.end()); // hamgiin tom elementiig butsaana.

int i1 = min_element(X.begin(), X.end()) – X.begin; // hamgiin baga elementiin index-g butsaana.



int v2 = *max_element(data, data+5); // Hamgiin tom elementiig butsaana.

int i3 = min_element(data, data+5) – data; // Hamgiin baga elementiin index-g butsaana.



Өөр нэг хэрэглээтэй зүйл бол тухайн өгөгдлийн төрлийг ологч typeof.

typeof(a+b) x = (a+b);

Үүнийг ашиглан дараах макро директивийг биччихвэл дараа дараагийн код-г хурдлуулах байх.



#define tr(container, it) \

for(typeof(container.begin()) it = container.begin(); it != container.end(); it++)

#define all(c) c.begin(), c.end()



Дараах маягаар ашиглана.


void f(const vector& v) {

int r = 0;

tr(v, it) {

r += (*it)*(*it);

}

return r;

}



insert:

vector < int > v;

// ...

v.insert(1, 42); // ehnii 42 shirheg elementiin daraa 1-g oruulj bn.



erase:

erase(iterator);

erase(iteratoriin ehlel, iteratoriin tugsgul);



String:

string s = "hello";

string

s1 = s.substr(0, 3), // "hel"

s2 = s.substr(1, 3), // "ell"

s3 = s.substr(0, s.length()-1), "hell"

s4 = s.substr(1); // "ello"



Set: Энэ нь бүх элемент нь ялгаатай байх 2тын хайлтын мод. Мөн 2тын мод нь дараалалгүй шугама бус мод бүтэцтэй тул индекс гэж байхгүй, тэгэхээр элементүүдтэй харьцахын тулд итератор хэрэглэхээс өөр арга байхгүй.

- Элемент нэмэх, Log(N).

- Элемент хасах, Lof(N).

- Бүх ялгаатай элемент тоолох, Log(N).

- Ямар нэгэн байгаа эсэхийг шалгах. Log(N).



set < int > s;



for(int i = 1; i <= 100; i++) {

s.insert(i);

}



s.insert(42); // 42 ali hediin baigaa tul yuch hiigdehgui!



for(int i = 2; i <= 100; i += 2) {

s.erase(i); // buh tegsh tonuudiig hasaj bn.

}



int n = int(s.size()); // 50.



int r = 0;

for(set < int > ::const_iterator it = s.begin(); it != s.end(); it++) {

r += *it;

} // 1-s 100 hurtleh buh sondgoi toonuudiin niilber bn.



int data[5] = { 5, 1, 4, 2, 3 };

set < int > S(data, data+5);



set< pair < string, pair < int, vector < int > > > SS;

int total = 0;

tr(SS, it) {

total += it->second.first;

}



if(s.find(42) != s.end()) {

// herev 42 baival

} else {

// 42 baihgui bol..

}



find функц ер нь асар их ашиглагдах байх, тэгэхээр дараах макро директивийг зарлачих хэрэгтэй.



#define present(container, element) (container.find(element) != container.end())

#define cpresent(container, element) (find(all(container),element) != container.end())



Векторын элементийн давхар элементийг хасаж эрэмлэхийг дараах маягаар маш хялбар хийж болно.

vector < int > v;

// …

set < int > s(all(v));

vector < int > v2(all(s));



Map: Map ерөнхийдөө бол Set. Ялгаа нь 2тын модны орой бүр түлхүүр үгтэй, тиймээс индекс маркаар ашиглаж бас болно.




map < string, int > M;

M["Top"] = 1;

M["Coder"] = 2;

M["SRM"] = 10;


int x = M["Top"] + M["Coder"];



if(M.find("SRM") != M.end()) {

M.erase(M.find("SRM")); // bas M.erase("SRM") bolno.

}



map < string, int > M;

// …

int r = 0;

tr(M, it) {

r += it->second;

}



Энэ удаагийн постоо энэ хүрээд өндөрлөе. Дараагийн парт2 гэж посто байгаа ба тэрүүгээрэй арай ахисан түвшинд хэрхэн ашиглах талаар заая.



Ашигласан материалууд:

http://en.cppreference.com/w/cpp/algorithm

http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=standardTemplateLibrary

Saturday, April 12, 2014

Network maximum flow, Ford-Fulkerson алгоритм маш энгийнээр, Монголын Үндэсний Оюутны Програмчлалын Олимпиад 14 анализ.

За сайн байцгаана уу. Хичээл их байгаа зав болохоороо пост бичнэ гээд байгаад байвал боломж бол гарахгүй юм байна ер нь. Тэгэхээр хамаагүй маш товч товч бичээд явья гэж бодлоо.

Үндэсний програмчлалын тэмцээн 4-н сарын 10-н 11-нд болсон. Одоогоор надад бүх бодлогууд нь ирээгүй байна, гэхдээ энэ тэмцээнд миний нэг бодлого сонгогдсон ба уг бодлогыхаа анализыг хийнэ. Гэснээс ганц ч баг бодоогүй байна лээ.

Үүнээс өмнө товчхон нетворк флов - урсгалын бодлогыг тайлбарлая.

Урсгалын бодлого маш олон янзаар гарч ирдэг. Жишээ нь хотын замын сүлжээ өгөгдсөн байгаа8 мөн зам бүр дээр явах машины тоог хязгаартай, энэ хязгаарууд нь бидэнд өгөгдсөн. Тэгвэл А гэсэн хотоос В гэсэн хотруу хамгийн ихдээ хэдэн машин явуулж болох вэ? Энэ бодлогыг Maximum Flow гэж нэрлэдэг.

Өөр нэгэн жишээ, Хотын сүлжээ замууд өгөгдсөн. Та тодорхой хэдэн тооны зам устгаж А В гэсэн 2 хотыг хооронд нь хүрэх боломжгүй болгох гэж байгаа. Зам бүрийг устгахад тодорхой зардалтай, таны даалгавар уг гарах зардалыг хамгийн бага байлгах юм. Энэ бодлогыг Minimum cut гэж нэрлэдэг.



Дээрх 2 бодлого 2уул өөр харагдаж байгаа ч, гайхалтай теорем, хариу яг ижил гардаг. Өөрөөр хэлвэл ямар нэгэн эерэг жинтэй чиглэлт графын хувьд, А оройгоос В оройн хувьд MaximumFlow = MinCut байдаг.


Жишээг зургаар харуулая.





s гэсэн оройгоос t гэсэн орой хүрэв хамгийн их урсгал 23 буюу дараах замуудыг ашигласан байх нь.





Одоо тэгвэл мөн 2 оройн хоорондахь MinCut-г олоё. Дээрх хэлсэн теоремоор хамгийн их урсгалтай тэнцүү 23 буюу дараах замуудыг устгах нь.





Алгоритмаа тайлбарлая

Өгөгдсөн графын хувьд s-с t хүрдэг ямар нэгэн замыг авч үзье, мөн үүнийгээ дэд зам гэж нэрлэе. Энэ нь бидэнд уг замд ашиглагдаж байгаа жингүүд бас мэдэгдэж байгаа ба эдгээрийгээ w1 w2 ... wk гэе. Тэгвэл энэ олсон замаар хамгийн ихдээ хэдий хэмжээний урсгал урсуулж болох вэ? энэ асуултын хариу min(w1, w2, ..., wk). Энэ хариуг урсгалын дэд хэмжээ гэе.
Дээрх жишээний хувьд дараах дэд замууд байж болох ба тухай бүрийх нь дэд урсгалууд нь хамгийн бага утгаад нь байхнээ.






Алгоритмд ашиглагдах ба нэгэн ухагдахууныг тайлбарлая


Ирмэгийг эргүүлэх


C(u -> v)-ээр хоёр хөрш u, v оройн хоорондахь боломжит урсгалын хэмжээг тэмдэглэе, мэдээж энэ чиглэлтэй шүү.
Тэгвэл ямар нэгэн дэд урсгал f уг хоёр хөрш оройгоор дамжвал

C(u -> v)-г f-ээр багасгаад

C(v -> u)-г f-ээр нэмэгдүүлэе.

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

За одоо дараах үйлдэлийг боломжгүй болтол нь хийе.

0 flow = 0

1 Хэрэв олдвол ямар нэгэн дэд зам ол, DFS.

2 Дэд урсгалыг ол flow-н утгыг уг утгаараа нэмэгдүүл.

3 Дэд зам-г бүрдүүлж байгаа бүх (u -> v)-н хувьд C(u->v)-г дэд урсгалаар багасгаж, C(v->u)-г дэд урсгалаар нэмэгдүүл.

4 1рүү оч.


За хө ингээд болоо. Дээрх алгоритмыг Ford-Fulkerson гэдэг, олсон хүнийх нь нэр. Хугацааны үнэлгээний хувьд дэд замыг O(V+E), мөн бид ядаж 1 урсгалыг дамжуулж байгаа тэгэхээр O((V+E)*flow). Энэ ер нь бол ихэнх тохиолдолд хангалттай үнэлгээ уг бодлогын хувьд.


MinCut бодлогын хувьд утгыг нь тэгээд олчихдог юм байж. Яаж устгагдсан ирмэгүүдийг олох вэ? энэ бодлогыг энэ талаар судлаж сонирхож байгаа хүмүүст өөрсөдөд нь үлдээе, энгийн амархан олдно. Туслах үүднээс, дээр хэрэглэгдэж байгаа С гэсэн хүснэгт бодлогын хариуг олсны дараа ямархуу байдалтай болж гэдэгийг ажиглаараа, энэ хүснэгтээс ирмэгийг олно.
Дараа дараагийхаа постоор хэрхэн алгоритмаа сайжруулах мөн жишээ бодлогуудын бодолт харуулая.


За одоо өөрийн бодлогоруугаа ороё.
Бодлогын өгүүлбэрийг энд дарж уншина уу.


Бодлого бол s гэсэн оройгоос t гэсэн орой хүртэл хоорондоо аль ч ирмэг оройгоор огтлолцоогүй байх 2 зам олох ба замуудын нийлбэр хамгийн бага байх ёстой. Энэ бодлогонд s=1, t=V гэж бэхлэсэн байгаа.


d(s,u)-ээр s оройгоос u хүрэх хамгийн богино замын хэмжээг тэмдэглэе. w(u,v)-ээр хэрэв u, v гэсэн орой хоорондоо u->v гэх чиглэлээр холбогдсон хөрш оройнууд бол тухайн ирмэгийн жинг тэмдэглэе.


Одоо s-ээс богино замын алгоритм Дижкстра хийн богино замуудыг олоё. Ингэхдээ бид s-дээр оройтой Т гэсэн модыг байгуулж чадна. Уг модон дээр байгаа u гэсэн орой бүрийн хувьд бид s->u богино замыг мэдэж чадна, энд мэдээж эд нар нь хөрш байх албагүй. P1-ээр T модондээр орших s->t замыг гэж үзье, бас мод байна шүү. Одоо графыхаа бүх w(u, v) ирмэгийн хувьд дараах өөрчлөлтийг хийе. w'(u,v) = w(u,v) − d(s,v) + d(s,u). Ингэхдээ Т модондээр байгаа бүх ирмэгүүдийн жинг 0 байхаар мөн Т модонд харъяалагдахгүй ирмэгүүдийн хувьд сөрөг тоо байхааргүй зохицуулаарай.

Одоо байгаа мэдээллээ ашиглан дахин шинэ граф байгуулая. Шинэ граф нь s-рүү орсон ирмэгийг хасна, мөн P1 модонд харьяалагдаж байгаа 0 утгатай ирмэгүүдийн хувьд чиглэлийг нь урвуулна.

Шинэ граф дээрээ дахин s-ээс эхлэн дахин Дижкстра хийн s->t богино замыг илэрхийлэх модыг олоод P2 гэе. P1 P2 хоюуланд нь харьяалагдах урвуулсан ирмэгийг хасая. Үлдсэн граф бодлогын хариу! энд 0 урттай цикл оршин байж болно. Бодлогын бүтэн хариу утга юу байх вэ? үүнийг уншигчдад үлдээе.


UPDATE
Жишээ болгон алгоритмын ажиллах процессыг доорх зурган жишээгээр харууллаа. Одоо бүр ойлгомжтой байх.



Бодлогыг ЭНД дарж бодно уу.

Нэгэнд энэ пост маань урсгалаар эхэлсэн учир энэ бодлогыг урсгалаар бодогдож болно, би гэхдээ одоо постоо үргэлжлүүлж чадахгүй нь, бодоход туслах нэг санаа нэмээд дараагийн урсгалын жишээ бодлогууд, нэмэлт сайжруулалт дээр бодолтыг тайлбарлая.


Vk гэсэн орой бүрийн хувьд (Vk(in) -> Vk(out)) гэсэн өөрчлөлт хийн графаа өргөтгөөд үз, ийм техник ер нь бол огтлож болохгүй гэсэн хязгаарлалтад тусладаг.



Жич: Монголоороо алдаатай бичсэн бол уучлаарай.

Sunday, April 6, 2014

2014 оны улсын мэдээлэл зүйн олимпиадын 2 ын даваа багш нарын бодлогууд

За сайн байцгаана уу, багш нарын бодлогийн анализийг хийх гэж үзлээ хэхэ

Бодлогуудын өгүүлбэрийг энд дараад харж болно.



Эхний бодлого "Геометр прогресс" -ийн хувьд (1<=k<m<=100)  тул k аас m дүгээр тоонуудаа үүсгэж байгаад өгөгдсөн N ширхэг тооноосоо хайгаад тоолж болно. N<=100000 тул шууд хайхад амжина. Гэхдээ N тоонуудаа эрэмбэлж байгаад бинар хайлт хийгээд тоолвол илүү хурдасна. Бичсэн source оо хамт хийв.




#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>

using namespace std;

long long member, A[100005];
int n, b, q, k, m, sum, i;

bool search(long long num)
{
int x, y, z;

x = 0;
y = n-1;
while (x <= y)
{
z = (x+y)/2;
if (A[z] == num) return 1;
if (num < A[z]) y = z-1;
else x = z+1;
}
return 0;
}

main()
{
freopen("geometer.in", "r", stdin);
freopen("geometer.out", "w", stdout);

scanf("%d %d %d",&n,&b,&q);
scanf("%d %d",&k,&m);
for (i=0; i<n; i++)
scanf("%lld",&A[i]);
sort(A,A+n);
member = b;
for (i=2; i<=k; i++)
member = member*q;

sum = 0;
for (i=k; i<=m; i++)
{
if (search(member)) sum++;
member = member*q;
if (member > A[n-1]) break;
}
printf("%d\n",sum);

}




Дараагийн бодлого "16 суурьтай палиндром тоо" -н дээр өгөгдсөн тоон дээрээ 1 ээс эхлээд ижил хэмжээгээр нэмж үүссэн тоогоо 16 тын тооллын системд шилжүүлээд палиндром эсэхийг шалгаад дараа нь хасч шалгаад явахад  хугацаандаа амжина гэж бодож байна.

Жишээ нь : n тоо өгөгдөх үед n+1 -ийг шалгаад дараа нь n-1 ииг шалгаад дараа нь n+2 , n-2 , n+3 гэх мэт шалгаад хамгийн эхэнд бодлогийн нөхцөлийг хангасан нь манай шийд болно гэсэн үг.






#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>

using namespace std;

vector <int> a;

bool checker(int x)
{
int i, l;
a.clear();
while (x != 0)
{
a.push_back(x%16);
x = x/16;
}
l = a.size();
for (i=0; i<l/2; i++)
if (a[i] != a[l-i-1]) return 0;
return 1;
}

main()
{
int i, t, num, j;

freopen("palind16.in", "r", stdin);
freopen("palind16.out", "w", stdout);

scanf("%d", &t);
for (i=1; i<=t; i++)
{
scanf("%d", &num);
j = -1;
while (i)
{
j++;
if (checker(num+j)) { printf("%d\n", num+j); break;}
if (checker(num-j) && num-j>0) { printf("%d\n", num-j); break;}
}
}
}




Сүүлчийн бодлого "Битийн дараалал" - ын хувьд DP хэрэглэж бодож болно. A[0][i] гэдэг нь 0 оор төгссөн i урттай дарааллын тоо ба A[1][i] нь 1 ээр төгссөн i урттай дарааллын тоог хадгалдаг гэж үзъе. Эндээс манай хариу A[0][N]+A[1][N] байна гэсэн үг. A[0][i] нь i-1 урттай 0 ээр төгссөн боломжуудын ард 0 ийг нэмж боломж үүсгэж болно. Мөн i-1 урттай 1 ээр төгссөн боломжуудын ард 0 ийг нэмж боломж үүсгэж ба эндээс боломж давхардаж тоологдохгүй нь харагдаж байна. Иймд A[0][i]=A[0][i-1]+A[1][i-1] гэсэн үг. Харин A[1][i] нь i-1 урттай 0 ээр төгссөн боломжуудын ард 1 ийг нэмж боломжуудыг үүсгэж болно. Харин i-1 урттай 1 ээр төгссөн боломжуудын ард 1 нэмж боломж үүсгэж болохгүй. Учир нь бодлогийн нөхцөлд 1 -ийн цифр дараалж орж болохгүй. Иймд A[1][i]=A[0][i] гэсэн үг. Эндээс бичээд үзэхэд N тооны хувьд бодлогийн хариу N+2 дахь фибоначчийн харагдана. Гэхдээ (1<=N<=100) тул урт тооны үйлдэл хийнэ.






#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>

using namespace std;

int A[3][50]={0}, n, i, j, i0, i1, i2, d, dig0, dig1, dig2, r, ii;

main()
{
freopen("bit.in", "r", stdin);
freopen("bit.out", "w", stdout);

scanf("%d",&n);

A[0][0] = 1; dig0 = 0;
A[1][0] = 1; dig1 = 0;
i0 = 0; i1 = 1; i2 = 2;
for (i=0; i<n; i++)
{
r = 0;
dig2 = max(dig0, dig1);
for (j=0; j<=dig2; j++)
{
A[i2][j] = (A[i1][j] + A[i0][j] + r)%10;
r = (A[i1][j] + A[i0][j] + r)/10;
}
if (r) { dig2++; A[i2][dig2]=r; }
ii = i0; i0 = i1; i1 = i2; i2= ii;
d = dig0; dig0 = dig1; dig1 = dig2; dig2 = d;
}
for (i = dig1; i>=0; i--)
printf("%d",A[i1][i]);
}




Анх удаагаа анализ хийж байгаа болхоор алдаа гаргасан байж магад .

Бичсэн Б. Гантүшиг ;)






Thursday, January 2, 2014

Open Cup - 2014 тэмцээний тухай.

UPDATE: Тэмцээнийг онлайнаар явуулах нь аль дээрээс төлөвлөгдсөн байсан ба эрт зарлаагүй нь шалтгаантай байсан. Гэхдээ энд онлайнаар оролцож байгаа оролцогчид тэмцээний дүнд нөлөөлөхгүй болохыг анхаарна уу. Онлайн тэмцээн тэмцээн болох өдөр буюу 1-р сарын 11-ны өглөө 11:15 am-д болно, Монголын цаг шүү. Өөр улс оронд байгаа хүмүүс цагаа ЭНД дар харна уу.

Тэмцээн ЭНЭ хаяг дээр болох болно.


Мөн онлайнд оролцох оролцогчид hackerrank.com-d өөрсдийн нэр болон хаягаар бүртгүүлж, бүртгүүлсэн UserID, last name, first name-үүдээ baasanbatp@gmail.com - руу илгээнэ үү. Илгээхдээ захианы нэрийн талбарт ONLINE PARTICPANT гэж гарчиглана уу. Ингэж бүртгүүлээгүй оролцогчийг тэмцээний дундуур ban-дах болно.


----------------------------


Тэмцээний удирдамжийг ЭНД дарж мөн бүртгүүлэх анкетыг ЭНД дарж татаж авна уу. Анкетаа бөглчөөд baasanbatp@gmail.com-руу илгээнэ үү.