├── README.md ├── first_round.cpp ├── sec_round.cpp ├── third_round.cpp └── third_round_update.cpp /README.md: -------------------------------------------------------------------------------- 1 | # 成绩 2 | 3 | 队伍:一方通行 4 | 5 | 初赛:0.2136s (西北Rank 16) 6 | 7 | 复赛:7.5473s (西北Rank 2) 8 | 9 | 决赛:674.9344s (总榜Rank 21) 10 | 11 | # 初赛:有向图找环(环长度为3-7) 12 | 13 | #### 一些Trick: 14 | 15 | ①:ID大于5w的点直接舍弃 && 不需要对ID做哈希映射,也能得到正确答案 16 | 17 | ②:尽量不要使用STL及一些库函数,手动实现会快很多 18 | 19 | ③:建正向图和反向图,在正向图中,如果当前点的邻接点数目大于1,将邻接点按ID排序,后续处理会方便很多 20 | 21 | ④:评测机并不会检测第一行的环路数量,随便输出一个数字即可 22 | 23 | 24 | #### 找环思路: 25 | 26 | ①:采用“3+4”模式:反向图BFS搜3层,存储合法的搜索路径以及长度为3的环路;正向图DFS搜4层,搜索中利用反向图已有结果直接拼接,即可得到长度为4-7的环路 27 | 28 | ②:使用8线程搜寻环路,任务片分配方式为手动指定(调参) 29 | 30 | 31 | #### 收获: 32 | 33 | ①:对不需要改变的量,使用:const Type &tmp 会更高效 34 | 35 | ②:对需要内联的小函数使用强制Inline 36 | 37 | ③:从ddd热身赛开源代码,学会了mmap的初步使用,以及多进程编程的基本思路 38 | 39 | 40 | # 复赛:有向图找环(A榜环长为3-7,B榜当天改为3-8) 41 | 42 | #### 变动:相较于初赛,转账记录从300w条扩充至2000w,同时限制转账前后路径的金额浮动为:0.2-3 43 | 44 | 总体思路沿用初赛,不同点是: 45 | 46 | ①:多线程mmap读取数据 47 | 48 | ②:对ID做哈希映射 49 | 50 | ③:采用反向三层标记 + 正向7层For寻找环路(初赛使用更为简洁的DFS,但是速度不如For嵌套,毕竟越丑越快) 51 | 52 | ④:找完一个点的环路后,立即将结果转换为字符串类型存储,加快输出(但是大佬使用int存储+流水线转换、输出会更快) 53 | 54 | ⑤:任务分配采用原子量抢占处理节点 55 | 56 | ⑥:复赛开始检测第一行的环路数量 57 | 58 | 59 | #### B榜当天需求改动:输入金额从uint32变为浮点数(小数点后最多两位),环路从3-7改为3-8 60 | 61 | ①:针对数据类型变动,由于只需要计算转账金额的前后比值,因此将所有读取金额×100,转换为整型即可,使用uint64存储 62 | 63 | ②:针对环路长度增加到8:采用反向4层标记 + 正向8层For寻找环路(这里是个大坑:官方临时增加的8环数量很少,因此反向标记3层会更快一些) 64 | 65 | Ps:B榜需求改动太大,与A榜官方的导向不同,导致很多A榜的优秀选手翻车,所以基本只要正常出分就可以入围决赛 66 | 67 | 68 | # 决赛:计算有向图中每个点的中介中心性,取top100输出 69 | 70 | #### 总体思路: 71 | 72 | ①:建图采用京津一霸Waven佬的前向星结构 73 | 74 | ②:官方1图为刺猬图,根据此特点,使用拓扑排序预处理:拓扑到的点,若出度为1,则此点不用Dijk算法,直接递推可得其中心性(递推公式在代码注释中) 75 | 76 | ③:通用优化算法,Dijk + 堆 77 | 78 | ④:面向数据集编程:建图时统计最大边权和边权和:若最大边权小于2000,使用uint16存储Dijk的距离(能过评测集也是很魔幻),若边权和小于INT32_MAX,使用uint32存储,否则使用uint64存储 79 | 80 | 81 | 82 | ### 感谢江山赛区的q佬,在比赛过程中,给了我很多悉心指导。在这里祝你毕业快乐,工作顺利。 83 | 84 | ### 感谢这次比赛遇到的每一位大佬,我有三句话想对你们说:“牛的” “懂了” “学到了” --老龙王 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /first_round.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | //#define TEST 8 | 9 | #define Inline __inline__ __attribute__((always_inline)) 10 | 11 | struct X { 12 | char t, o; 13 | }; 14 | 15 | #define P(T) T, '0', T, '1', T, '2', T, '3', T, '4', T, '5', T, '6', T, '7', T, '8', T, '9' 16 | 17 | static const X s_pairs[] = { P('0'), P('1'), P('2'), P('3'), P('4'), P('5'), P('6'), P('7'), P('8'), P('9') }; 18 | 19 | #define W(N, I) *(X*)&b[N] = s_pairs[I] 20 | 21 | #define A(N) t = (uint64_t(1) << (32 + N / 5 * N * 53 / 16)) / uint32_t(1e##N) + 1 + N/6 - N/8, t *= u, t >>= N / 5 * N * 53 / 16, t += N / 6 * 4, W(0, t >> 32) 22 | 23 | #define S(N) b[N] = char(uint64_t(10) * uint32_t(t) >> 32) + '0' 24 | 25 | #define D(N) t = uint64_t(100) * uint32_t(t), W(N, t >> 32) 26 | 27 | #define L0 b[0] = char(u) + '0' 28 | 29 | #define L1 W(0, u) 30 | 31 | #define L2 A(1), S(2) 32 | 33 | #define L3 A(2), D(2) 34 | 35 | #define L4 A(3), D(2), S(4) 36 | 37 | #define L5 A(4), D(2), D(4) 38 | 39 | #define L6 A(5), D(2), D(4), S(6) 40 | 41 | #define L7 A(6), D(2), D(4), D(6) 42 | 43 | #define L8 A(7), D(2), D(4), D(6), S(8) 44 | 45 | #define L9 A(8), D(2), D(4), D(6), D(8) 46 | 47 | #define LN(N) (L##N, b += N + 1) 48 | 49 | #define LZ(N) (L##N, length = N) 50 | 51 | #define LG(F) (u<100 ? u<10 ? F(0) : F(1) : u<10000 ? u<1000 ? F(2) : F(3) : F(4)) 52 | 53 | 54 | Inline int myItoa(uint32_t u, char* b){ 55 | int length; 56 | uint64_t t; 57 | LG(LZ); 58 | return length + 1; 59 | } 60 | 61 | Inline int myAtoi(char* s){ 62 | char* tmp = s; 63 | register int ans = 0; 64 | do{ 65 | ans = (ans<<3) + (ans<<1); 66 | ans += (*tmp - 48); 67 | tmp++; 68 | }while(47 < *tmp && *tmp < 58); 69 | return ans; 70 | } 71 | 72 | int rawData[560000]; 73 | int indexOfRawData; 74 | 75 | Inline void readTestData(const string& fileName){ 76 | #ifdef TEST 77 | auto start = std::chrono::steady_clock::now(); 78 | #endif 79 | int fd = open(fileName.c_str(), O_RDONLY); 80 | int length = lseek(fd, 0, SEEK_END); 81 | char* buf = (char*)mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); 82 | char* bg = buf; 83 | const char* ed = buf + length; 84 | register int index; 85 | char tmp[10]; 86 | register int i; 87 | do{ 88 | for(i = 0; i < 3; ++i){ 89 | index = 0; 90 | while(*(bg+index) != ',' && *(bg+index) != '\n') index++; 91 | if(i < 2){ 92 | memset(tmp, ' ', 10); 93 | memcpy(tmp, bg, index); 94 | rawData[indexOfRawData++] = myAtoi(tmp); 95 | } 96 | bg += (index + 1); 97 | } 98 | }while(bg < ed); 99 | #ifdef TEST 100 | auto end = std::chrono::steady_clock::now(); 101 | double dr_ms = std::chrono::duration(end-start).count(); 102 | cout << "read test data time: " << dr_ms << " ms\n"; 103 | #endif 104 | } 105 | 106 | const int MAX_ID_NUM = 50000; 107 | int Graph[MAX_ID_NUM][50]; 108 | int indexOfGraph[MAX_ID_NUM]; 109 | int VerGraph[MAX_ID_NUM][50]; 110 | int indexOfVerGraph[MAX_ID_NUM]; 111 | int collectionOfGraphNode[MAX_ID_NUM]; 112 | int indexOfCOGN; 113 | 114 | Inline void buildGraph(){ 115 | #ifdef TEST 116 | auto start = std::chrono::steady_clock::now(); 117 | #endif 118 | register int i; 119 | for(i = 0; i < indexOfRawData; i+=4){ 120 | if(rawData[i] < MAX_ID_NUM && rawData[i+1] < MAX_ID_NUM){ 121 | Graph[rawData[i]][indexOfGraph[rawData[i]]++] = rawData[i+1]; 122 | VerGraph[rawData[i+1]][indexOfVerGraph[rawData[i+1]]++] = rawData[i]; 123 | } 124 | if(rawData[i+2] < MAX_ID_NUM && rawData[i+3] < MAX_ID_NUM){ 125 | Graph[rawData[i+2]][indexOfGraph[rawData[i+2]]++] = rawData[i+3]; 126 | VerGraph[rawData[i+3]][indexOfVerGraph[rawData[i+3]]++] = rawData[i+2]; 127 | } 128 | } 129 | for(i = 0; i < MAX_ID_NUM; ++i){ 130 | if(indexOfGraph[i] != 0) { 131 | collectionOfGraphNode[indexOfCOGN++] = i; 132 | if(indexOfGraph[i] > 1) sort(Graph[i], Graph[i] + indexOfGraph[i]); 133 | } 134 | } 135 | #ifdef TEST 136 | auto end = std::chrono::steady_clock::now(); 137 | double dr_ms = std::chrono::duration(end-start).count(); 138 | cout << "build graph time: " << dr_ms << " ms\n"; 139 | #endif 140 | } 141 | 142 | 143 | const int THREAD_NUM = 8; 144 | int Result0[THREAD_NUM][3 * 500000]; 145 | int Result1[THREAD_NUM][4 * 500000]; 146 | int Result2[THREAD_NUM][5 * 1000000]; 147 | int Result3[THREAD_NUM][6 * 2000000]; 148 | int Result4[THREAD_NUM][7 * 3000000]; 149 | int* threadResult[5][THREAD_NUM] = {{Result0[0], Result0[1], Result0[2], Result0[3],Result0[4], Result0[5], Result0[6], Result0[7]}, 150 | {Result1[0], Result1[1], Result1[2], Result1[3],Result1[4], Result1[5], Result1[6], Result1[7]}, 151 | {Result2[0], Result2[1], Result2[2], Result2[3],Result2[4], Result2[5], Result2[6], Result2[7]}, 152 | {Result3[0], Result3[1], Result3[2], Result3[3],Result3[4], Result3[5], Result3[6], Result3[7]}, 153 | {Result4[0], Result4[1], Result4[2], Result4[3],Result4[4], Result4[5], Result4[6], Result4[7]}}; 154 | int indexOfTR[5][THREAD_NUM]; 155 | int threadNodeList[THREAD_NUM][MAX_ID_NUM]; 156 | int indexOfTNL[THREAD_NUM]; 157 | 158 | void searchResult(const int& curNode, int depth, int* nodeVec, bool* visited, bool* linkable, const int& tid, vector>* Bridge){ 159 | nodeVec[depth - 1] = curNode; 160 | visited[curNode] = true; 161 | register int index, i, j, tmp; 162 | for(index = 0; index < indexOfGraph[curNode]; ++index) if(Graph[curNode][index] >= nodeVec[0]) break; 163 | for(; index < indexOfGraph[curNode]; ++index){ 164 | tmp = Graph[curNode][index]; 165 | if(!visited[tmp]){ 166 | if(linkable[tmp]){ 167 | for(i = 0; i < Bridge[tmp].size(); ++i){ 168 | if(visited[Bridge[tmp][i].first] || visited[Bridge[tmp][i].second]) continue; 169 | for(j = 0; j < depth; ++j) threadResult[depth][tid][indexOfTR[depth][tid]++] = nodeVec[j]; 170 | threadResult[depth][tid][indexOfTR[depth][tid]++] = tmp; 171 | threadResult[depth][tid][indexOfTR[depth][tid]++] = Bridge[tmp][i].first; 172 | threadResult[depth][tid][indexOfTR[depth][tid]++] = Bridge[tmp][i].second; 173 | } 174 | } 175 | if(depth < 4 && indexOfGraph[tmp] != 0) searchResult(tmp, depth + 1, nodeVec, visited, linkable, tid, Bridge); 176 | } 177 | } 178 | visited[curNode] = false; 179 | } 180 | 181 | Inline bool myCmp(const int a[2], const int b[2]){ 182 | if(a[0] != b[0]) return a[0] < b[0]; 183 | else return a[1] < b[1]; 184 | } 185 | 186 | Inline bool dicsort(const pair& a, const pair& b){ 187 | if(a.first != b.first) return a.first < b.first; 188 | else return a.second < b.second; 189 | } 190 | 191 | Inline void getResultByThread(const int& tid){ 192 | #ifdef TEST 193 | auto start = std::chrono::steady_clock::now(); 194 | #endif 195 | int nodeVec[4]; 196 | bool visited[MAX_ID_NUM]{false}; 197 | bool linkable[MAX_ID_NUM]{false}; 198 | vector>* Br = new vector>[MAX_ID_NUM]; 199 | int recover[MAX_ID_NUM]; 200 | int indexOfRecover = 0; 201 | int i, j, k, x; 202 | int ii, jj, kk; 203 | int* res3[2500]; 204 | int indexOfRes3 = 0; 205 | for (i = 0; i < indexOfTNL[tid]; ++i){ 206 | ii = threadNodeList[tid][i]; 207 | if(indexOfGraph[ii] != 0) { 208 | for(j = 0; j < indexOfVerGraph[ii]; ++j){ 209 | jj = VerGraph[ii][j]; 210 | if(jj > ii){ 211 | for(k = 0; k < indexOfVerGraph[jj]; ++k){ 212 | kk = VerGraph[jj][k]; 213 | if(kk > ii){ 214 | for(x = 0; x < indexOfVerGraph[kk]; ++x){ 215 | if(VerGraph[kk][x] == ii){ 216 | res3[indexOfRes3] = new int[2]; 217 | res3[indexOfRes3][0] = kk; 218 | res3[indexOfRes3][1] = jj; 219 | indexOfRes3++; 220 | } 221 | else if(VerGraph[kk][x] > ii && VerGraph[kk][x] != jj){ 222 | if(Br[VerGraph[kk][x]].empty()){ 223 | linkable[VerGraph[kk][x]] = true; 224 | recover[indexOfRecover++] = VerGraph[kk][x]; 225 | } 226 | Br[VerGraph[kk][x]].emplace_back(make_pair(kk, jj)); 227 | } 228 | } 229 | } 230 | } 231 | } 232 | } 233 | if(indexOfRes3 != 0){ 234 | if(indexOfRes3 > 1) sort(res3, res3 + indexOfRes3, myCmp); 235 | for(j = 0; j < indexOfRes3; ++j){ 236 | threadResult[0][tid][indexOfTR[0][tid]++] = ii; 237 | threadResult[0][tid][indexOfTR[0][tid]++] = res3[j][0]; 238 | threadResult[0][tid][indexOfTR[0][tid]++] = res3[j][1]; 239 | } 240 | indexOfRes3 = 0; 241 | } 242 | if(indexOfRecover != 0){ 243 | for(j = 0; j < indexOfRecover; ++j) { 244 | if(Br[recover[j]].size() > 1) sort(Br[recover[j]].begin(), Br[recover[j]].end(), dicsort); 245 | } 246 | searchResult(ii, 1, nodeVec, visited, linkable, tid, Br); 247 | for(j = 0; j < indexOfRecover; ++j) { 248 | Br[recover[j]].clear(); 249 | linkable[recover[j]] = false; 250 | } 251 | indexOfRecover = 0; 252 | } 253 | } 254 | } 255 | #ifdef TEST 256 | auto end = std::chrono::steady_clock::now(); 257 | double dr_ms = std::chrono::duration(end-start).count(); 258 | cout << "thread: " << tid << " get result time: " << dr_ms << " ms\n"; 259 | #endif 260 | } 261 | 262 | 263 | Inline void getResult(){ 264 | int i, j; 265 | int mu = 100; 266 | int para[8] = {3, 5, 8, 13, 17, 25, 38, 100}; 267 | int pre_p = 0; 268 | for(i = 0; i < THREAD_NUM; ++i){ 269 | int p = para[i] * indexOfCOGN / mu; 270 | for(j = pre_p; j < p; ++j){ 271 | threadNodeList[i][indexOfTNL[i]++] = collectionOfGraphNode[j]; 272 | } 273 | pre_p = p; 274 | } 275 | thread threads[THREAD_NUM]; 276 | for(i = 0; i < THREAD_NUM; ++i){ 277 | threads[i] = thread(getResultByThread, i); 278 | } 279 | for(i = 0; i < THREAD_NUM; ++i){ 280 | threads[i].join(); 281 | } 282 | } 283 | 284 | 285 | struct Bi{ 286 | int cycLen; 287 | int left, right; 288 | char* ans = NULL; 289 | int length = 0; 290 | }; 291 | 292 | Inline void transforRes(Bi& bi, int* Result){ 293 | bi.ans = new char[(bi.right - bi.left) * 11]; 294 | char* p = bi.ans; 295 | int l; 296 | for(int i = bi.left; i < bi.right; ++i){ 297 | l = myItoa(Result[i], p); 298 | p += l; 299 | bi.length += l; 300 | if((i + 1) % bi.cycLen == 0){ 301 | *(p++) = '\n'; 302 | bi.length++; 303 | } 304 | else { 305 | *(p++) = ','; 306 | bi.length++; 307 | } 308 | } 309 | } 310 | 311 | 312 | Inline void saveResult(const string& fileName){ 313 | #ifdef TEST 314 | auto start = std::chrono::steady_clock::now(); 315 | #endif 316 | #ifdef TEST 317 | int totalResultNum = 0; 318 | for(int i = 0; i < 5; ++i){ 319 | for(int j = 0; j < THREAD_NUM; ++j){ 320 | totalResultNum += indexOfTR[i][j] / (i + 3); 321 | } 322 | } 323 | cout << "result size: " << totalResultNum << '\n'; 324 | #endif 325 | FILE* fp; 326 | fp = fopen(fileName.c_str(), "w+"); 327 | char tit[2] = {'1', '\n'}; 328 | fwrite(tit, sizeof(char), 2, fp); 329 | const int TN_Trans = 8; 330 | int i, k, j; 331 | for(i = 0; i < 5; ++i){ 332 | for(k = 0; k < THREAD_NUM; k++){ 333 | if(indexOfTR[i][k] < 5000){ 334 | char* ans = new char[indexOfTR[i][k] * 11]; 335 | char* p = ans; 336 | int l; 337 | int length(0); 338 | for(int ii = 0; ii < indexOfTR[i][k]; ++ii){ 339 | l = myItoa(threadResult[i][k][ii], p); 340 | p += l; 341 | length += l; 342 | if((ii + 1) % (i+3) == 0){ 343 | *(p++) = '\n'; 344 | length++; 345 | } 346 | else { 347 | *(p++) = ','; 348 | length++; 349 | } 350 | } 351 | fwrite(ans, sizeof(char), length, fp); 352 | } 353 | else{ 354 | thread threads[TN_Trans]; 355 | Bi bi[TN_Trans]; 356 | int pre_p = 0; 357 | for(j = 0; j < TN_Trans; ++j){ 358 | int po = (indexOfTR[i][k]/(i+3)) * (j+1)/TN_Trans * (i+3); 359 | bi[j].cycLen = i + 3; 360 | bi[j].left = pre_p; 361 | bi[j].right = po; 362 | pre_p = po; 363 | threads[j] = thread(transforRes, ref(bi[j]), threadResult[i][k]); 364 | } 365 | for(j = 0; j < TN_Trans; ++j) threads[j].join(); 366 | for(j = 0; j < TN_Trans; ++j) { 367 | fwrite(bi[j].ans, sizeof(char), bi[j].length, fp); 368 | } 369 | } 370 | } 371 | } 372 | fclose(fp); 373 | #ifdef TEST 374 | auto end = std::chrono::steady_clock::now(); 375 | double dr_ms = std::chrono::duration(end-start).count(); 376 | cout << "output result time: " << dr_ms << " ms\n"; 377 | #endif 378 | } 379 | 380 | 381 | int main() { 382 | string testFile = "/data/test_data.txt"; 383 | string resultFile = "/projects/student/result.txt"; 384 | #ifdef TEST 385 | auto start = std::chrono::steady_clock::now(); 386 | #endif 387 | readTestData(testFile); 388 | buildGraph(); 389 | getResult(); 390 | saveResult(resultFile); 391 | #ifdef TEST 392 | auto end = std::chrono::steady_clock::now(); 393 | double dr_ms = std::chrono::duration(end - start).count(); 394 | cout << "total cost time: " << dr_ms << " ms\n"; 395 | #endif 396 | return 0; 397 | } 398 | -------------------------------------------------------------------------------- /sec_round.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | //#define TEST 8 | 9 | #define Inline __inline__ __attribute__((always_inline)) 10 | 11 | struct X { 12 | char t, o; 13 | }; 14 | 15 | #define P(T) T, '0', T, '1', T, '2', T, '3', T, '4', T, '5', T, '6', T, '7', T, '8', T, '9' 16 | 17 | static const X s_pairs[] = { P('0'), P('1'), P('2'), P('3'), P('4'), P('5'), P('6'), P('7'), P('8'), P('9') }; 18 | 19 | #define W(N, I) *(X*)&b[N] = s_pairs[I] 20 | 21 | #define A(N) t = (uint64_t(1) << (32 + N / 5 * N * 53 / 16)) / uint32_t(1e##N) + 1 + N/6 - N/8, t *= u, t >>= N / 5 * N * 53 / 16, t += N / 6 * 4, W(0, t >> 32) 22 | 23 | #define S(N) b[N] = char(uint64_t(10) * uint32_t(t) >> 32) + '0' 24 | 25 | #define D(N) t = uint64_t(100) * uint32_t(t), W(N, t >> 32) 26 | 27 | #define L0 b[0] = char(u) + '0' 28 | 29 | #define L1 W(0, u) 30 | 31 | #define L2 A(1), S(2) 32 | 33 | #define L3 A(2), D(2) 34 | 35 | #define L4 A(3), D(2), S(4) 36 | 37 | #define L5 A(4), D(2), D(4) 38 | 39 | #define L6 A(5), D(2), D(4), S(6) 40 | 41 | #define L7 A(6), D(2), D(4), D(6) 42 | 43 | #define L8 A(7), D(2), D(4), D(6), S(8) 44 | 45 | #define L9 A(8), D(2), D(4), D(6), D(8) 46 | 47 | #define LN(N) (L##N, b += N + 1) 48 | 49 | #define LZ(N) (L##N, length = N) 50 | 51 | #define LG(F) (u<100 ? u<10 ? F(0) : F(1) : u<1000000 ? u<10000 ? u<1000 ? F(2) : F(3) : u<100000 ? F(4) : F(5) : u<100000000 ? u<10000000 ? F(6) : F(7) : u<1000000000 ? F(8) : F(9)) 52 | 53 | Inline uint8_t myItoa(uint32_t u, char* b){ 54 | uint8_t length; 55 | uint64_t t; 56 | LG(LZ); 57 | return length + 1; 58 | } 59 | 60 | Inline uint32_t myAtoi(char* s){ 61 | char* t = s; 62 | uint32_t ans = 0; 63 | do{ 64 | ans = (ans<<3) + (ans<<1); 65 | ans += (*t - 48); 66 | t++; 67 | }while(47 < *t && *t < 58); 68 | return ans; 69 | } 70 | 71 | Inline uint64_t myAtoi1(char* s){ 72 | char* t = s; 73 | uint64_t ans = 0; 74 | do{ 75 | ans = (ans<<3) + (ans<<1); 76 | ans += (*t - 48); 77 | t++; 78 | }while(47 < *t && *t < 58); 79 | ans *= 100; 80 | if(*t == '.'){ 81 | t++; 82 | ans += (*t-48) * 10; 83 | t++; 84 | if(47 < *t && *t < 58){ 85 | ans += (*t-48); 86 | } 87 | } 88 | return ans; 89 | } 90 | 91 | const uint8_t THREAD_NUM = 8; 92 | uint32_t nodeCnt; 93 | char* buf; 94 | vector unhashID; 95 | vector nodeData[THREAD_NUM]; 96 | vector edgeData[THREAD_NUM]; 97 | struct Bi{ 98 | uint32_t tid; 99 | uint32_t l, r; 100 | }; 101 | vector>> Graph; 102 | vector>> VerGraph; 103 | 104 | 105 | Inline void readTestData(Bi& bi){ 106 | char* bg = buf + bi.l; 107 | const char* ed = buf + bi.r; 108 | char tmp[15]; 109 | nodeData[bi.tid].reserve(1000000); 110 | edgeData[bi.tid].reserve(500000); 111 | do{ 112 | for(uint8_t i = 0; i < 3; ++i){ 113 | uint8_t index = 0; 114 | while(*(bg+index) != ',' && *(bg+index) != '\n') index++; 115 | memset(tmp, ' ', 15); 116 | memcpy(tmp, bg, index); 117 | i < 2 ? nodeData[bi.tid].emplace_back(myAtoi(tmp)) : edgeData[bi.tid].emplace_back(myAtoi1(tmp)); 118 | bg += (index + 1); 119 | } 120 | }while(bg < ed); 121 | } 122 | 123 | Inline void read(const string& fileName){ 124 | #ifdef TEST 125 | auto start = std::chrono::steady_clock::now(); 126 | #endif 127 | int fd = open(fileName.c_str(), O_RDONLY); 128 | uint32_t length = lseek(fd, 0, SEEK_END); 129 | buf = (char*)mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); 130 | thread threads[THREAD_NUM]; 131 | Bi bi[THREAD_NUM]; 132 | uint32_t pre_p = 0; 133 | for(uint8_t i = 0; i < THREAD_NUM; ++i){ 134 | uint32_t p = (i + 1) * length / THREAD_NUM; 135 | while (buf[p - 1] != '\n') --p; 136 | bi[i].l = pre_p; 137 | bi[i].r = p; 138 | pre_p = p; 139 | bi[i].tid = i; 140 | threads[i] = thread(readTestData, ref(bi[i])); 141 | } 142 | for(uint8_t i = 0; i < THREAD_NUM; ++i){ 143 | threads[i].join(); 144 | } 145 | #ifdef TEST 146 | auto end = std::chrono::steady_clock::now(); 147 | double dr_ms = std::chrono::duration(end-start).count(); 148 | cout << "read test data time: " << dr_ms << " ms\n"; 149 | #endif 150 | } 151 | 152 | Inline void buildGraph(){ 153 | #ifdef TEST 154 | auto start = std::chrono::steady_clock::now(); 155 | #endif 156 | vector nodeD; 157 | vector edgeD; 158 | for(uint8_t i = 0; i < THREAD_NUM; ++i){ 159 | nodeD.insert(nodeD.end(), nodeData[i].begin(), nodeData[i].end()); 160 | edgeD.insert(edgeD.end(), edgeData[i].begin(), edgeData[i].end()); 161 | } 162 | vector tmp = nodeD; 163 | sort(tmp.begin(), tmp.end()); 164 | tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end()); 165 | nodeCnt = tmp.size(); 166 | Graph = vector>>(nodeCnt); 167 | VerGraph = vector>>(nodeCnt); 168 | unhashID = tmp; 169 | unordered_map hashID; 170 | for(uint32_t i = 0; i < nodeCnt; ++i) 171 | hashID[tmp[i]] = i; 172 | uint32_t send, send1, recv, recv1; 173 | for(uint32_t i = 0, j = 0; i < nodeD.size(); i+=4, j+=2){ 174 | send = hashID[nodeD[i]]; 175 | recv = hashID[nodeD[i+1]]; 176 | Graph[send].emplace_back(make_tuple(recv, edgeD[j])); 177 | VerGraph[recv].emplace_back(make_tuple(send, edgeD[j])); 178 | send1 = hashID[nodeD[i+2]]; 179 | recv1 = hashID[nodeD[i+3]]; 180 | Graph[send1].emplace_back(make_tuple(recv1, edgeD[j+1])); 181 | VerGraph[recv1].emplace_back(make_tuple(send1, edgeD[j+1])); 182 | } 183 | for(uint32_t i = 0; i < nodeCnt; ++i){ 184 | if(Graph[i].size() > 1) sort(Graph[i].begin(), Graph[i].end()); 185 | } 186 | #ifdef TEST 187 | auto end = std::chrono::steady_clock::now(); 188 | double dr_ms = std::chrono::duration(end-start).count(); 189 | cout << "build graph time: " << dr_ms << " ms\n"; 190 | #endif 191 | } 192 | 193 | atomic thNode(0); 194 | const uint32_t MAX_ID = 4000000; 195 | uint8_t tidBelong[MAX_ID]; 196 | bool checkCycle[6][MAX_ID]{false}; 197 | uint32_t beginPos[6][MAX_ID]; 198 | uint32_t len[6][MAX_ID]; 199 | 200 | struct RES{ 201 | uint32_t resNum = 0; 202 | uint32_t index[6]; 203 | char* Res0 = new char[33 * 1000000]; 204 | char* Res1 = new char[44 * 1000000]; 205 | char* Res2 = new char[55 * 1000000]; 206 | char* Res3 = new char[66 * 2000000]; 207 | char* Res4 = new char[77 * 3000000]; 208 | char* Res5 = new char[88 * 5000000]; 209 | char* Res[6] = {Res0, Res1, Res2, Res3, Res4, Res5}; 210 | }res[THREAD_NUM]; 211 | 212 | Inline bool judgeW(const uint64_t a, const uint64_t b){ 213 | return a <= ((b<<2) + b) && b <= ((a<<1) + a); 214 | } 215 | 216 | Inline void convert(const uint8_t tid, const uint8_t cycLen, uint32_t* nodeVec){ 217 | char* p = res[tid].Res[cycLen] + res[tid].index[cycLen]; 218 | for(uint8_t k = 0; k < cycLen+2; ++k){ 219 | uint8_t l = myItoa(unhashID[nodeVec[k]], p); 220 | p += l; 221 | res[tid].index[cycLen] += l; 222 | *(p++) = ','; 223 | res[tid].index[cycLen]++; 224 | } 225 | uint8_t l = myItoa(unhashID[nodeVec[cycLen+2]], p); 226 | p += l; 227 | res[tid].index[cycLen] += l; 228 | *(p) = '\n'; 229 | res[tid].index[cycLen]++; 230 | } 231 | 232 | void dfs(const uint32_t node, const uint8_t tid, uint32_t* nodeVec, bool* visited, uint8_t* distc){ 233 | nodeVec[0] = node; 234 | visited[node] = true; 235 | tidBelong[node] = tid; 236 | for(uint16_t i = 0; i < Graph[node].size(); ++i){ 237 | uint32_t ii = get<0>(Graph[node][i]); 238 | if(ii <= node) continue; 239 | else{ 240 | nodeVec[1] = ii; 241 | visited[ii] = true; 242 | for(uint16_t j = 0; j < Graph[ii].size(); ++j){ 243 | uint32_t jj = get<0>(Graph[ii][j]); 244 | if(jj <= node) continue; 245 | if(!judgeW(get<1>(Graph[node][i]), get<1>(Graph[ii][j]))) continue; 246 | if(!visited[jj]){ 247 | nodeVec[2] = jj; 248 | visited[jj] = true; 249 | for(uint16_t k = 0; k < Graph[jj].size(); ++k){ 250 | uint32_t kk = get<0>(Graph[jj][k]); 251 | if(kk < node) continue; 252 | if(!judgeW(get<1>(Graph[ii][j]), get<1>(Graph[jj][k]))) continue; 253 | if(kk == node){ 254 | if(judgeW(get<1>(Graph[jj][k]), get<1>(Graph[node][i]))){ 255 | if(!checkCycle[0][node]) { 256 | checkCycle[0][node] = true; 257 | beginPos[0][node] = res[tid].index[0]; 258 | } 259 | convert(tid, 0, nodeVec); 260 | res[tid].resNum++; 261 | } 262 | continue; 263 | } 264 | if(!visited[kk]){ 265 | nodeVec[3] = kk; 266 | visited[kk] = true; 267 | for(uint16_t l = 0; l < Graph[kk].size(); ++l){ 268 | uint32_t ll = get<0>(Graph[kk][l]); 269 | if(ll < node) continue; 270 | if(distc[ll] == 0) continue; 271 | if(!judgeW(get<1>(Graph[jj][k]), get<1>(Graph[kk][l]))) continue; 272 | if(ll == node){ 273 | if(judgeW(get<1>(Graph[kk][l]), get<1>(Graph[node][i]))){ 274 | if(!checkCycle[1][node]) { 275 | checkCycle[1][node] = true; 276 | beginPos[1][node] = res[tid].index[1]; 277 | } 278 | convert(tid, 1, nodeVec); 279 | res[tid].resNum++; 280 | } 281 | continue; 282 | } 283 | if(!visited[ll]){ 284 | nodeVec[4] = ll; 285 | visited[ll] = true; 286 | for(uint16_t z = 0; z < Graph[ll].size(); ++z){ 287 | uint32_t zz = get<0>(Graph[ll][z]); 288 | if(zz < node) continue; 289 | if(distc[zz] == 0 || distc[zz] == 4) continue; 290 | if(!judgeW(get<1>(Graph[kk][l]), get<1>(Graph[ll][z]))) continue; 291 | if(zz == node){ 292 | if(judgeW(get<1>(Graph[ll][z]), get<1>(Graph[node][i]))){ 293 | if(!checkCycle[2][node]) { 294 | checkCycle[2][node] = true; 295 | beginPos[2][node] = res[tid].index[2]; 296 | } 297 | convert(tid, 2, nodeVec); 298 | res[tid].resNum++; 299 | } 300 | continue; 301 | } 302 | if(!visited[zz]){ 303 | nodeVec[5] = zz; 304 | visited[zz] = true; 305 | for(uint16_t x = 0; x < Graph[zz].size(); ++x){ 306 | uint32_t xx = get<0>(Graph[zz][x]); 307 | if(xx < node) continue; 308 | if(distc[xx] == 0 || distc[xx] == 3 || distc[xx] == 4) continue; 309 | if(!judgeW(get<1>(Graph[ll][z]), get<1>(Graph[zz][x]))) continue; 310 | if(xx == node) { 311 | if (judgeW(get<1>(Graph[zz][x]), get<1>(Graph[node][i]))) { 312 | if(!checkCycle[3][node]) { 313 | checkCycle[3][node] = true; 314 | beginPos[3][node] = res[tid].index[3]; 315 | } 316 | convert(tid, 3, nodeVec); 317 | res[tid].resNum++; 318 | } 319 | continue; 320 | } 321 | if(!visited[xx]){ 322 | nodeVec[6] = xx; 323 | visited[xx] = true; 324 | for(uint16_t c = 0; c < Graph[xx].size(); ++c){ 325 | uint32_t cc = get<0>(Graph[xx][c]); 326 | if(cc < node) continue; 327 | if(distc[cc] == 0 || distc[cc] == 2 || distc[cc] == 3 || distc[cc] == 4) continue; 328 | if(!judgeW(get<1>(Graph[zz][x]), get<1>(Graph[xx][c]))) continue; 329 | if(cc == node) { 330 | if (judgeW(get<1>(Graph[xx][c]), get<1>(Graph[node][i]))) { 331 | if(!checkCycle[4][node]) { 332 | checkCycle[4][node] = true; 333 | beginPos[4][node] = res[tid].index[4]; 334 | } 335 | convert(tid, 4, nodeVec); 336 | res[tid].resNum++; 337 | } 338 | continue; 339 | } 340 | if(!visited[cc]){ 341 | nodeVec[7] = cc; 342 | for(uint16_t v = 0; ; ++v){ 343 | if(get<0>(Graph[cc][v]) < node) continue; 344 | if(!judgeW(get<1>(Graph[xx][c]), get<1>(Graph[cc][v]))) break; 345 | if(judgeW(get<1>(Graph[cc][v]), get<1>(Graph[node][i]))){ 346 | if(!checkCycle[5][node]) { 347 | checkCycle[5][node] = true; 348 | beginPos[5][node] = res[tid].index[5]; 349 | } 350 | convert(tid, 5, nodeVec); 351 | res[tid].resNum++; 352 | } 353 | break; 354 | } 355 | } 356 | } 357 | visited[xx] = false; 358 | } 359 | } 360 | visited[zz] = false; 361 | } 362 | } 363 | visited[ll] = false; 364 | } 365 | } 366 | visited[kk] = false; 367 | } 368 | } 369 | visited[jj] = false; 370 | } 371 | } 372 | visited[ii] = false; 373 | } 374 | } 375 | visited[node] = false; 376 | for(uint8_t i = 0; i < 6; ++i){ 377 | len[i][node] = res[tid].index[i] - beginPos[i][node]; 378 | } 379 | } 380 | 381 | Inline void getThreadResult(const uint8_t tid){ 382 | uint32_t nodeVec[8]; 383 | bool visited[nodeCnt]{false}; 384 | uint8_t distc[nodeCnt]; 385 | uint32_t* recov = new uint32_t[nodeCnt]; 386 | uint32_t indexOfRecov = 0; 387 | while(1){ 388 | uint32_t tmp = thNode++; 389 | if(tmp >= nodeCnt) break; 390 | if(Graph[tmp].size() != 0){ 391 | distc[tmp] = 5; 392 | recov[indexOfRecov++] = tmp; 393 | for(uint16_t j = 0; j < VerGraph[tmp].size(); ++j){ 394 | uint32_t jj = get<0>(VerGraph[tmp][j]); 395 | if(jj <= tmp) continue; 396 | else{ 397 | if(distc[jj] == 0) { 398 | distc[jj] = 1; 399 | recov[indexOfRecov++] = jj; 400 | } 401 | else if(distc[jj] >= 2) distc[jj] = 1; 402 | for(uint16_t k = 0; k < VerGraph[jj].size(); ++k){ 403 | uint32_t kk = get<0>(VerGraph[jj][k]); 404 | if(kk <= tmp) continue; 405 | if(!judgeW(get<1>(VerGraph[jj][k]), get<1>(VerGraph[tmp][j]))) continue; 406 | else{ 407 | if(distc[kk] == 0){ 408 | distc[kk] = 2; 409 | recov[indexOfRecov++] = kk; 410 | } 411 | else if(distc[kk] >= 3) distc[kk] = 2; 412 | for(uint16_t x = 0; x < VerGraph[kk].size(); ++x){ 413 | uint32_t xx = get<0>(VerGraph[kk][x]); 414 | if(xx <= tmp || xx == jj) continue; 415 | if(!judgeW(get<1>(VerGraph[kk][x]), get<1>(VerGraph[jj][k]))) continue; 416 | if(distc[xx] == 0) { 417 | distc[xx] = 3; 418 | recov[indexOfRecov++] = xx; 419 | } 420 | else if(distc[xx] == 4) distc[xx] = 3; 421 | for(uint16_t c = 0; c < VerGraph[xx].size(); ++c){ 422 | uint32_t cc = get<0>(VerGraph[xx][c]); 423 | if(cc <= tmp || cc == kk || cc == jj) continue; 424 | if(!judgeW(get<1>(VerGraph[xx][c]), get<1>(VerGraph[kk][x]))) continue; 425 | if(distc[cc] == 0){ 426 | distc[cc] = 4; 427 | recov[indexOfRecov++] = cc; 428 | } 429 | } 430 | } 431 | } 432 | } 433 | } 434 | } 435 | dfs(tmp, tid, nodeVec, visited, distc); 436 | for(uint32_t j = 0; j < indexOfRecov; ++j) distc[recov[j]] = 0; 437 | indexOfRecov = 0; 438 | } 439 | } 440 | } 441 | 442 | Inline void getResult(){ 443 | #ifdef TEST 444 | auto start = std::chrono::steady_clock::now(); 445 | #endif 446 | thread threads[THREAD_NUM]; 447 | for(uint8_t i = 0; i < THREAD_NUM; ++i){ 448 | threads[i] = thread(getThreadResult, i); 449 | } 450 | for(uint8_t i = 0; i < THREAD_NUM; ++i){ 451 | threads[i].join(); 452 | } 453 | #ifdef TEST 454 | auto end = std::chrono::steady_clock::now(); 455 | double dr_ms = std::chrono::duration(end-start).count(); 456 | cout << "get result time: " << dr_ms << " ms\n"; 457 | #endif 458 | } 459 | 460 | 461 | Inline void saveResult(const string& fileName){ 462 | #ifdef TEST 463 | auto start = std::chrono::steady_clock::now(); 464 | #endif 465 | uint32_t totalResultNum = 0; 466 | for(uint32_t i = 0; i < THREAD_NUM; ++i){ 467 | totalResultNum += res[i].resNum; 468 | } 469 | #ifdef TEST 470 | cout << "result size: " << totalResultNum << '\n'; 471 | #endif 472 | FILE* fp; 473 | fp = fopen(fileName.c_str(), "w"); 474 | char* title = new char[20]; 475 | sprintf(title, "%d\n", totalResultNum); 476 | fwrite(title, sizeof(char), strlen(title), fp); 477 | for(uint8_t i = 0; i < 6; ++i){ 478 | for(uint32_t j = 0; j < nodeCnt; ++j){ 479 | if(!checkCycle[i][j]) continue; 480 | fwrite(res[tidBelong[j]].Res[i]+beginPos[i][j], sizeof(char), len[i][j], fp); 481 | } 482 | } 483 | fclose(fp); 484 | #ifdef TEST 485 | auto end = std::chrono::steady_clock::now(); 486 | double dr_ms = std::chrono::duration(end-start).count(); 487 | cout << "output result time: " << dr_ms << " ms\n"; 488 | #endif 489 | } 490 | 491 | 492 | int main() { 493 | string testFile = "/data/test_data.txt"; 494 | string resultFile = "/projects/student/result.txt"; 495 | #ifdef TEST 496 | auto start = std::chrono::steady_clock::now(); 497 | #endif 498 | read(testFile); 499 | buildGraph(); 500 | getResult(); 501 | saveResult(resultFile); 502 | #ifdef TEST 503 | auto end = std::chrono::steady_clock::now(); 504 | double dr_ms = std::chrono::duration(end - start).count(); 505 | cout << "total cost time: " << dr_ms << " ms\n"; 506 | #endif 507 | return 0; 508 | } 509 | -------------------------------------------------------------------------------- /third_round.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | //#define TEST 8 | 9 | #define Inline __inline__ __attribute__((always_inline)) 10 | 11 | typedef uint8_t ui8; 12 | typedef uint16_t ui16; 13 | typedef uint32_t ui32; 14 | typedef uint64_t ui64; 15 | 16 | /** 17 | * 定义: 18 | * 线程数目 19 | * 映射后节点数目 20 | * 反哈希数组 21 | * 多线程读取文件的必要信息 22 | */ 23 | const ui8 THREAD_NUM = 12; 24 | ui32 g_node_cnt; 25 | char* g_buf; 26 | struct Bi{ 27 | ui32 tid; 28 | ui32 l; 29 | ui32 r; 30 | }; 31 | vector g_unhash_id; 32 | vector g_node_data[THREAD_NUM]; 33 | vector g_edge_data[THREAD_NUM]; 34 | 35 | 36 | /** 37 | * 整型转换为字符型函数 38 | * @param s 39 | * @return 40 | */ 41 | Inline ui32 MyAtoi(char* s){ 42 | ui32 ans = 0; 43 | while(47 < *s && *s < 58){ 44 | ans *= 10; 45 | ans += (*s - 48); 46 | s++; 47 | } 48 | return ans; 49 | } 50 | 51 | /** 52 | * 多线程读取数据的工作函数 53 | * @param bi 54 | */ 55 | void ReadJob(Bi& bi){ 56 | char* bg = g_buf + bi.l; 57 | const char* ed = g_buf + bi.r; 58 | char tmp[16]; 59 | g_node_data[bi.tid].reserve(1000000); 60 | g_edge_data[bi.tid].reserve(500000); 61 | ui32 line[3]; 62 | while(bg < ed){ 63 | for(ui32 & ii : line){ 64 | ui8 index = 0; 65 | while(*(bg+index) != ',' && *(bg+index) != '\n') index++; 66 | memset(tmp, ' ', 16); 67 | memcpy(tmp, bg, 16); 68 | ii = MyAtoi(tmp); 69 | bg += (index + 1); 70 | } 71 | // 去掉金额为0转账记录 72 | if(line[2] != 0){ 73 | g_node_data[bi.tid].emplace_back(line[0]); 74 | g_node_data[bi.tid].emplace_back(line[1]); 75 | g_edge_data[bi.tid].emplace_back(line[2]); 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * 多线程读取数据的任务分配函数 82 | * @param file_name 83 | */ 84 | void ReadData(const string& file_name){ 85 | #ifdef TEST 86 | auto start = std::chrono::steady_clock::now(); 87 | #endif 88 | int fd = open(file_name.c_str(), O_RDONLY); 89 | ui32 length = lseek(fd, 0, SEEK_END); 90 | g_buf = (char*)mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); 91 | thread threads[THREAD_NUM]; 92 | Bi bi[THREAD_NUM]; 93 | ui32 pre_p = 0; 94 | for(ui8 i = 0; i < THREAD_NUM; ++i){ 95 | ui32 p = (i + 1) * length / THREAD_NUM; 96 | while (p > 0 && g_buf[p - 1] != '\n') --p; 97 | bi[i].l = pre_p; 98 | bi[i].r = p; 99 | pre_p = p; 100 | bi[i].tid = i; 101 | threads[i] = thread(ReadJob, ref(bi[i])); 102 | } 103 | for(auto & thread : threads){ 104 | thread.join(); 105 | } 106 | #ifdef TEST 107 | auto end = std::chrono::steady_clock::now(); 108 | double dr_ms = std::chrono::duration(end-start).count(); 109 | cout << "[read test data time]: " << dr_ms << " ms\n"; 110 | #endif 111 | } 112 | 113 | 114 | /** 115 | * 定义: 116 | * 最大点数目,最大边数目 117 | * 点的出度,出度开始位置 118 | * 点的入度,入度开始位置 119 | * 前向星数据结构 120 | */ 121 | const ui32 MAX_NODE = 2500000; 122 | const ui32 MAX_EDGE = 2500000; 123 | ui32 g_max_edge; 124 | ui64 g_sum_edge; 125 | ui16 g_ind[MAX_NODE]; 126 | ui16 g_outd[MAX_NODE]; 127 | ui32 g_ind_bg[MAX_NODE]; 128 | ui32 g_head[MAX_NODE]; 129 | struct Edge { 130 | ui32 to; 131 | ui32 money; 132 | }; 133 | Edge g_graph[MAX_EDGE]; 134 | 135 | 136 | /** 137 | * 建图函数: 138 | * 合并线程读取的数据,排序、去重 139 | * 获得映射后节点数目,反哈希数组 140 | * 哈希原始节点 141 | * 使用前向星建图 142 | */ 143 | void BuildGraph(){ 144 | #ifdef TEST 145 | auto start = std::chrono::steady_clock::now(); 146 | #endif 147 | // 数据处理 148 | vector node_d; 149 | for(auto & it : g_node_data){ 150 | node_d.insert(node_d.end(), it.begin(), it.end()); 151 | } 152 | vector tmp = node_d; 153 | sort(tmp.begin(), tmp.end()); 154 | tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end()); 155 | g_node_cnt = tmp.size(); 156 | g_unhash_id = tmp; 157 | unordered_map hash_id; 158 | for(ui32 i = 0; i < g_node_cnt; ++i) 159 | hash_id[tmp[i]] = i; 160 | // 开始建图 161 | ui32* curlen = new ui32[g_node_cnt + 1](); 162 | for(auto & it : g_node_data){ 163 | for(ui32 j = 0; j < it.size(); j+=2){ 164 | g_outd[hash_id[it[j]]]++; 165 | } 166 | } 167 | g_head[0] = 0; 168 | for(ui32 i = 1; i <= g_node_cnt; ++i){ 169 | g_head[i] = g_head[i - 1] + g_outd[i - 1]; 170 | } 171 | ui32 from, to, money; 172 | for(ui8 i = 0; i < THREAD_NUM; ++i){ 173 | for(ui32 j = 0, k = 0; j < g_node_data[i].size(); j+=2, ++k){ 174 | from = hash_id[g_node_data[i][j]]; 175 | to = hash_id[g_node_data[i][j+1]]; 176 | money = g_edge_data[i][k]; 177 | g_graph[g_head[from] + curlen[from]].to = to; 178 | g_graph[g_head[from] + curlen[from]++].money = money; 179 | g_ind[to]++; 180 | g_max_edge = g_max_edge < money ? money : g_max_edge; 181 | g_sum_edge += money; 182 | } 183 | } 184 | g_ind_bg[0] = 0; 185 | for(ui32 i = 1; i < g_node_cnt; ++i){ 186 | g_ind_bg[i] = g_ind_bg[i - 1] + g_ind[i-1]; 187 | } 188 | #ifdef TEST 189 | auto end = std::chrono::steady_clock::now(); 190 | double dr_ms = std::chrono::duration(end-start).count(); 191 | cout << "[build graph time]: " << dr_ms << " ms\n"; 192 | #endif 193 | } 194 | 195 | 196 | /** 197 | * 定义: 198 | * 不使用Dijk算法的节点标记 199 | * 前驱链中出度为1的链路数目总和 200 | * 前驱数组:当“前驱点”的“前驱链中出度为1的链路数目总和”大于0时,记录 201 | */ 202 | bool g_jump_over[MAX_NODE]; 203 | ui32 g_pre_single_outd_num[MAX_NODE]; 204 | vector g_pre_node[MAX_NODE]; 205 | 206 | 207 | /** 208 | * 拓扑排序函数: 209 | * 当拓扑到的点出度大于1时,直接拓扑排序即可 210 | * 当拓扑到的点出度为1时,标记其不使用Dijk算法,并酌情记录其他信息,继续拓扑排序 211 | */ 212 | void TopoSort(){ 213 | #ifdef TEST 214 | auto start = std::chrono::steady_clock::now(); 215 | #endif 216 | queue q; 217 | for(int i = 0; i < g_node_cnt; ++i){ 218 | if(0 == g_ind[i]) q.push(i); 219 | } 220 | while(!q.empty()){ 221 | int v = q.front(); 222 | q.pop(); 223 | if(g_outd[v] > 1){ 224 | const ui32 &l = g_head[v], &r = g_head[v+1]; 225 | Edge* e = &g_graph[l]; 226 | for(ui32 k = l; k < r; ++k, ++e) if(0 == --g_ind[e->to]) q.push(e->to); 227 | } 228 | else if(g_outd[v] == 1){ 229 | g_jump_over[v] = true; 230 | const ui32 &adj_node = g_graph[g_head[v]].to; 231 | g_pre_single_outd_num[adj_node] += (g_pre_single_outd_num[v] + 1); 232 | if(g_pre_single_outd_num[v] > 0) g_pre_node[adj_node].emplace_back(v); 233 | if(0 == --g_ind[adj_node]) q.push(adj_node); 234 | } 235 | } 236 | #ifdef TEST 237 | auto end = std::chrono::steady_clock::now(); 238 | double dr_ms = std::chrono::duration(end-start).count(); 239 | cout << "[topo sort time]: " << dr_ms << " ms\n"; 240 | #endif 241 | } 242 | 243 | 244 | /** 245 | * 定义: 246 | * [速度测试]总处理节点数目--原子量 247 | * 线程抢占的节点--原子量 248 | * 线程的关键中心性数组 249 | */ 250 | #ifdef TEST 251 | atomic cnt(0); 252 | #endif 253 | atomic g_th_node(0); 254 | double* g_bc[THREAD_NUM]; 255 | 256 | 257 | /** 258 | * 更新没有使用Dijk算法的点的关键中心性 259 | * 计算公式:对于当前点的前驱数组中记录的点u:g_bc[u] += g_pre_single_outd_num[u] * new_delta; 260 | * @param s 261 | * @param delta 262 | * @param tid 263 | */ 264 | void UpdataJumpOverNode(const ui32 &s, const double &new_delta, const ui8 &tid){ 265 | for(ui32 & u : g_pre_node[s]){ 266 | g_bc[tid][u] += g_pre_single_outd_num[u] * new_delta; 267 | UpdataJumpOverNode(u, new_delta + 1, tid); 268 | } 269 | } 270 | 271 | 272 | /** 273 | * class Use16ForDst: 274 | * 使用uint16记录Dijk的遍历距离 275 | */ 276 | class Use16ForDst{ 277 | public: 278 | struct Heap{ 279 | ui32 num; 280 | ui16 dist; 281 | bool operator < (const Heap &t) const { 282 | return dist > t.dist; 283 | } 284 | }; 285 | 286 | public: 287 | static void DijkWithHeap(const ui32& s, const ui8& tid, ui32* pred, ui16* len_of_pred, ui16* dst, ui16* sigma, double* delta, ui32* lst, bool* visit, Heap* heap) { 288 | int len_of_lst = 0; 289 | sigma[s] = 1; 290 | 291 | // Dijk算法 + 堆优化 292 | Heap tmp{}; 293 | tmp.num = s; 294 | tmp.dist = 0; 295 | heap[0] = tmp; 296 | ui32 heap_sz = 1; 297 | while(heap_sz) { 298 | pop_heap(heap, heap + heap_sz--); 299 | if(visit[heap[heap_sz].num]) continue; 300 | Heap u = heap[heap_sz]; 301 | const ui32 &v = u.num; 302 | visit[v] = true; 303 | lst[len_of_lst++] = v; 304 | const ui32 &l = g_head[v], &r = g_head[v+1]; 305 | Edge* e = &g_graph[l]; 306 | for(ui32 k = l; k < r; ++k, ++e){ 307 | const ui32 &adj_node = e->to; 308 | const ui16 &new_dist = u.dist + e->money; 309 | if(new_dist > dst[adj_node]) continue; 310 | if(!visit[adj_node] && new_dist < dst[adj_node]){ 311 | dst[adj_node] = new_dist; 312 | tmp.num = adj_node; 313 | tmp.dist = new_dist; 314 | heap[heap_sz++] = tmp; 315 | push_heap(heap, heap + heap_sz); 316 | len_of_pred[adj_node] = 0; 317 | pred[g_ind_bg[adj_node] + len_of_pred[adj_node]++] = v; 318 | sigma[adj_node] = sigma[v]; 319 | } 320 | else if(new_dist == dst[adj_node]) { 321 | pred[g_ind_bg[adj_node] + len_of_pred[adj_node]++] = v; 322 | sigma[adj_node] += sigma[v]; 323 | } 324 | } 325 | } 326 | 327 | // 更新本轮Dijk的关键中心性贡献值 328 | for(int i = len_of_lst - 1; i > 0; --i) { 329 | const ui32 &v = lst[i]; 330 | for(ui32 j = 0; j < len_of_pred[v]; ++j){ 331 | delta[pred[g_ind_bg[v] + j]] += (1.0 + delta[v]) * sigma[pred[g_ind_bg[v] + j]] / sigma[v]; 332 | } 333 | g_bc[tid][v] += ((g_pre_single_outd_num[s] + 1) * delta[v]); 334 | } 335 | g_bc[tid][s] += g_pre_single_outd_num[s] * delta[s]; 336 | if(!g_pre_node[s].empty()) UpdataJumpOverNode(s, delta[s] + 1, tid); 337 | 338 | // 为下一次Dijk初始化 339 | for(ui32 i = 0; i < len_of_lst; ++i) { 340 | const ui32 &v = lst[i]; 341 | dst[v] = INT16_MAX; 342 | sigma[v] = 0; 343 | delta[v] = 0.0; 344 | visit[v] = false; 345 | } 346 | } 347 | 348 | /** 349 | * 多线程Dijk的工作函数 350 | * 使用原子量抢占获得Dijk的起始点 351 | * [测试]每处理1000个点,打印一下 352 | * @param tid 353 | */ 354 | static void GetBc(const ui8 tid) { 355 | bool* visit = new bool[g_node_cnt](); 356 | ui16* sigma = new ui16[g_node_cnt](); 357 | ui16* len_of_pred = new ui16[g_node_cnt]; 358 | ui32* pred = new ui32[MAX_EDGE]; 359 | ui32* lst = new ui32[g_node_cnt]; 360 | ui16* dst = new ui16[g_node_cnt]; 361 | auto* delta = new double[g_node_cnt](); 362 | Heap* heap = new Heap[MAX_NODE]; 363 | for(ui32 i = 0; i < g_node_cnt; ++i){ 364 | dst[i] = INT16_MAX; 365 | } 366 | while(true){ 367 | ui32 node = g_th_node++; 368 | if(node >= g_node_cnt) break; 369 | if(g_jump_over[node]) continue; 370 | DijkWithHeap(node, tid, pred, len_of_pred, dst, sigma, delta, lst, visit, heap); 371 | #ifdef TEST 372 | ui32 t = ++cnt; 373 | if(t % 1000 == 0 || t == g_node_cnt) printf("%d/%d\n", t, g_node_cnt); 374 | #endif 375 | } 376 | delete [] visit; 377 | delete [] sigma; 378 | delete [] len_of_pred; 379 | delete [] pred; 380 | delete [] lst; 381 | delete [] dst; 382 | delete [] delta; 383 | delete [] heap; 384 | } 385 | 386 | /** 387 | * 多线程Dijk任务分配函数 388 | */ 389 | static void AllocTask(){ 390 | #ifdef TEST 391 | auto start = std::chrono::steady_clock::now(); 392 | #endif 393 | thread threads[THREAD_NUM]; 394 | for(ui8 i = 0; i < THREAD_NUM; ++i){ 395 | g_bc[i] = new double[g_node_cnt](); 396 | threads[i] = thread(Use16ForDst::GetBc, i); 397 | } 398 | for(auto & thread : threads){ 399 | thread.join(); 400 | } 401 | #ifdef TEST 402 | auto end = std::chrono::steady_clock::now(); 403 | double dr_ms = std::chrono::duration(end - start).count(); 404 | cout << "[calculate time]: " << dr_ms << " ms\n"; 405 | #endif 406 | } 407 | }; 408 | 409 | /** 410 | * class Use32ForDst: 411 | * 使用uint32记录Dijk的遍历距离 412 | */ 413 | class Use32ForDst{ 414 | public: 415 | struct Heap{ 416 | ui32 num; 417 | ui32 dist; 418 | bool operator < (const Heap &t) const { 419 | return dist > t.dist; 420 | } 421 | }; 422 | 423 | public: 424 | static void DijkWithHeap(const ui32& s, const ui8& tid, ui32* pred, ui16* len_of_pred, ui32* dst, ui16* sigma, double* delta, ui32* lst, bool* visit, Heap* heap) { 425 | int len_of_lst = 0; 426 | sigma[s] = 1; 427 | 428 | // Dijk算法 + 堆优化 429 | Heap tmp{}; 430 | tmp.num = s; 431 | tmp.dist = 0; 432 | heap[0] = tmp; 433 | ui32 heap_sz = 1; 434 | while(heap_sz) { 435 | pop_heap(heap, heap + heap_sz--); 436 | if(visit[heap[heap_sz].num]) continue; 437 | Heap u = heap[heap_sz]; 438 | const ui32 &v = u.num; 439 | visit[v] = true; 440 | lst[len_of_lst++] = v; 441 | const ui32 &l = g_head[v], &r = g_head[v+1]; 442 | Edge* e = &g_graph[l]; 443 | for(ui32 k = l; k < r; ++k, ++e){ 444 | const ui32 &adj_node = e->to; 445 | const ui32 &new_dist = u.dist + e->money; 446 | if(new_dist > dst[adj_node]) continue; 447 | if(!visit[adj_node] && new_dist < dst[adj_node]){ 448 | dst[adj_node] = new_dist; 449 | tmp.num = adj_node; 450 | tmp.dist = new_dist; 451 | heap[heap_sz++] = tmp; 452 | push_heap(heap, heap + heap_sz); 453 | len_of_pred[adj_node] = 0; 454 | pred[g_ind_bg[adj_node] + len_of_pred[adj_node]++] = v; 455 | sigma[adj_node] = sigma[v]; 456 | } 457 | else if(new_dist == dst[adj_node]) { 458 | pred[g_ind_bg[adj_node] + len_of_pred[adj_node]++] = v; 459 | sigma[adj_node] += sigma[v]; 460 | } 461 | } 462 | } 463 | 464 | // 更新本轮Dijk的关键中心性贡献值 465 | for(int i = len_of_lst - 1; i > 0; --i) { 466 | const ui32 &v = lst[i]; 467 | for(ui32 j = 0; j < len_of_pred[v]; ++j){ 468 | delta[pred[g_ind_bg[v] + j]] += (1.0 + delta[v]) * sigma[pred[g_ind_bg[v] + j]] / sigma[v]; 469 | } 470 | g_bc[tid][v] += ((g_pre_single_outd_num[s] + 1) * delta[v]); 471 | } 472 | g_bc[tid][s] += g_pre_single_outd_num[s] * delta[s]; 473 | if(!g_pre_node[s].empty()) UpdataJumpOverNode(s, delta[s] + 1, tid); 474 | 475 | // 为下一次Dijk初始化 476 | for(ui32 i = 0; i < len_of_lst; ++i) { 477 | const ui32 &v = lst[i]; 478 | dst[v] = INT32_MAX; 479 | sigma[v] = 0; 480 | delta[v] = 0.0; 481 | visit[v] = false; 482 | } 483 | } 484 | 485 | /** 486 | * 多线程Dijk的工作函数 487 | * 使用原子量抢占获得Dijk的起始点 488 | * [测试]每处理1000个点,打印一下 489 | * @param tid 490 | */ 491 | static void GetBc(const ui8 tid) { 492 | bool* visit = new bool[g_node_cnt](); 493 | ui16* sigma = new ui16[g_node_cnt](); 494 | ui16* len_of_pred = new ui16[g_node_cnt]; 495 | ui32* pred = new ui32[MAX_EDGE]; 496 | ui32* lst = new ui32[g_node_cnt]; 497 | ui32* dst = new ui32[g_node_cnt]; 498 | auto* delta = new double[g_node_cnt](); 499 | Heap* heap = new Heap[MAX_NODE]; 500 | for(ui32 i = 0; i < g_node_cnt; ++i){ 501 | dst[i] = INT32_MAX; 502 | } 503 | while(true){ 504 | ui32 node = g_th_node++; 505 | if(node >= g_node_cnt) break; 506 | if(g_jump_over[node]) continue; 507 | DijkWithHeap(node, tid, pred, len_of_pred, dst, sigma, delta, lst, visit, heap); 508 | #ifdef TEST 509 | ui32 t = ++cnt; 510 | if(t % 1000 == 0 || t == g_node_cnt) printf("%d/%d\n", t, g_node_cnt); 511 | #endif 512 | } 513 | delete [] visit; 514 | delete [] sigma; 515 | delete [] len_of_pred; 516 | delete [] pred; 517 | delete [] lst; 518 | delete [] dst; 519 | delete [] delta; 520 | delete [] heap; 521 | } 522 | 523 | /** 524 | * 多线程Dijk任务分配函数 525 | */ 526 | static void AllocTask(){ 527 | #ifdef TEST 528 | auto start = std::chrono::steady_clock::now(); 529 | #endif 530 | thread threads[THREAD_NUM]; 531 | for(ui8 i = 0; i < THREAD_NUM; ++i){ 532 | g_bc[i] = new double[g_node_cnt](); 533 | threads[i] = thread(Use32ForDst::GetBc, i); 534 | } 535 | for(auto & thread : threads){ 536 | thread.join(); 537 | } 538 | #ifdef TEST 539 | auto end = std::chrono::steady_clock::now(); 540 | double dr_ms = std::chrono::duration(end - start).count(); 541 | cout << "[calculate time]: " << dr_ms << " ms\n"; 542 | #endif 543 | } 544 | }; 545 | 546 | /** 547 | * class Use64ForDst: 548 | * 使用uint64记录Dijk的遍历距离 549 | */ 550 | class Use64ForDst{ 551 | public: 552 | struct Heap{ 553 | ui32 num; 554 | ui64 dist; 555 | bool operator < (const Heap &t) const { 556 | return dist > t.dist; 557 | } 558 | }; 559 | 560 | public: 561 | static void DijkWithHeap(const ui32& s, const ui8& tid, ui32* pred, ui16* len_of_pred, ui64* dst, ui16* sigma, double* delta, ui32* lst, bool* visit, Heap* heap) { 562 | int len_of_lst = 0; 563 | sigma[s] = 1; 564 | 565 | 566 | Heap tmp{}; 567 | tmp.num = s; 568 | tmp.dist = 0; 569 | heap[0] = tmp; 570 | ui32 heap_sz = 1; 571 | while(heap_sz) { 572 | pop_heap(heap, heap + heap_sz--); 573 | if(visit[heap[heap_sz].num]) continue; 574 | Heap u = heap[heap_sz]; 575 | const ui32 &v = u.num; 576 | visit[v] = true; 577 | lst[len_of_lst++] = v; 578 | const ui32 &l = g_head[v], &r = g_head[v+1]; 579 | Edge* e = &g_graph[l]; 580 | for(ui32 k = l; k < r; ++k, ++e){ 581 | const ui32 &adj_node = e->to; 582 | const ui64 &new_dist = u.dist + e->money; 583 | if(new_dist > dst[adj_node]) continue; 584 | if(!visit[adj_node] && new_dist < dst[adj_node]){ 585 | dst[adj_node] = new_dist; 586 | tmp.num = adj_node; 587 | tmp.dist = new_dist; 588 | heap[heap_sz++] = tmp; 589 | push_heap(heap, heap + heap_sz); 590 | len_of_pred[adj_node] = 0; 591 | pred[g_ind_bg[adj_node] + len_of_pred[adj_node]++] = v; 592 | sigma[adj_node] = sigma[v]; 593 | } 594 | else if(new_dist == dst[adj_node]) { 595 | pred[g_ind_bg[adj_node] + len_of_pred[adj_node]++] = v; 596 | sigma[adj_node] += sigma[v]; 597 | } 598 | } 599 | } 600 | 601 | // 更新本轮Dijk的关键中心性贡献值 602 | for(int i = len_of_lst - 1; i > 0; --i) { 603 | const ui32 &v = lst[i]; 604 | for(ui32 j = 0; j < len_of_pred[v]; ++j){ 605 | delta[pred[g_ind_bg[v] + j]] += (1.0 + delta[v]) * sigma[pred[g_ind_bg[v] + j]] / sigma[v]; 606 | } 607 | g_bc[tid][v] += (g_pre_single_outd_num[s] + 1) * delta[v]; 608 | } 609 | g_bc[tid][s] += g_pre_single_outd_num[s] * delta[s]; 610 | if(!g_pre_node[s].empty()) UpdataJumpOverNode(s, delta[s] + 1, tid); 611 | 612 | // 为下一次Dijk初始化 613 | for(ui32 i = 0; i < len_of_lst; ++i) { 614 | const ui32 &v = lst[i]; 615 | dst[v] = INT64_MAX; 616 | sigma[v] = 0; 617 | delta[v] = 0.0; 618 | visit[v] = false; 619 | } 620 | } 621 | 622 | /** 623 | * 多线程Dijk的工作函数 624 | * 使用原子量抢占获得Dijk的起始点 625 | * [测试]每处理1000个点,打印一下 626 | * @param tid 627 | */ 628 | static void GetBc(const ui8 tid) { 629 | bool* visit = new bool[g_node_cnt](); 630 | ui16* sigma = new ui16[g_node_cnt](); 631 | ui16* len_of_pred = new ui16[g_node_cnt]; 632 | ui32* pred = new ui32[MAX_EDGE]; 633 | ui32* lst = new ui32[g_node_cnt]; 634 | ui64* dst = new ui64[g_node_cnt]; 635 | auto* delta = new double[g_node_cnt](); 636 | Heap* heap = new Heap[MAX_NODE]; 637 | for(ui32 i = 0; i < g_node_cnt; ++i){ 638 | dst[i] = INT64_MAX; 639 | } 640 | while(true){ 641 | ui32 node = g_th_node++; 642 | if(node >= g_node_cnt) break; 643 | if(g_jump_over[node]) continue; 644 | DijkWithHeap(node, tid, pred, len_of_pred, dst, sigma, delta, lst, visit, heap); 645 | #ifdef TEST 646 | ui32 t = ++cnt; 647 | if(t % 1000 == 0 || t == g_node_cnt) printf("%d/%d\n", t, g_node_cnt); 648 | #endif 649 | } 650 | delete [] visit; 651 | delete [] sigma; 652 | delete [] len_of_pred; 653 | delete [] pred; 654 | delete [] lst; 655 | delete [] dst; 656 | delete [] delta; 657 | delete [] heap; 658 | } 659 | 660 | /** 661 | * 多线程Dijk任务分配函数 662 | */ 663 | static void AllocTask(){ 664 | #ifdef TEST 665 | auto start = std::chrono::steady_clock::now(); 666 | #endif 667 | thread threads[THREAD_NUM]; 668 | for(ui8 i = 0; i < THREAD_NUM; ++i){ 669 | g_bc[i] = new double[g_node_cnt](); 670 | threads[i] = thread(Use64ForDst::GetBc, i); 671 | } 672 | for(auto & thread : threads){ 673 | thread.join(); 674 | } 675 | #ifdef TEST 676 | auto end = std::chrono::steady_clock::now(); 677 | double dr_ms = std::chrono::duration(end - start).count(); 678 | cout << "[calculate time]: " << dr_ms << " ms\n"; 679 | #endif 680 | } 681 | }; 682 | 683 | 684 | /** 685 | * 精度控制函数:用于std::sort 686 | * @param a 687 | * @param b 688 | * @return 689 | */ 690 | Inline bool PrecisionCmp(const pair &a, const pair &b){ 691 | if(abs(a.first - b.first) > 0.0001) return a.first > b.first; 692 | else return a.second < b.second; 693 | } 694 | 695 | /** 696 | * 输出函数:汇总多线程的关键中心性,排序,取top100输出 697 | * @param result_file 698 | */ 699 | void Write(const string &result_file){ 700 | auto* bc_final = new double[g_node_cnt](); 701 | for(auto & ii : g_bc){ 702 | for(ui32 j = 0; j < g_node_cnt; ++j){ 703 | bc_final[j] += ii[j]; 704 | } 705 | } 706 | auto res = new pair[g_node_cnt]; 707 | for(ui32 i = 0; i < g_node_cnt; ++i){ 708 | res[i] = make_pair(bc_final[i], i); 709 | } 710 | sort(res, res + g_node_cnt, PrecisionCmp); 711 | ui8 index = g_node_cnt < 100 ? g_node_cnt : 100; 712 | ofstream write(result_file.c_str(), ios::out); 713 | for(ui8 i = 0; i < index; ++i){ 714 | write << g_unhash_id[res[i].second] << ',' << fixed << setprecision(3) << res[i].first << '\n'; 715 | } 716 | write.close(); 717 | delete [] bc_final; 718 | delete [] res; 719 | } 720 | 721 | 722 | /** 723 | * 主函数依次执行: 724 | * 1、读取函数 725 | * 2、建图函数 726 | * 3、拓扑排序函数 727 | * 4、选择Dijk类 728 | * 5、输出函数 729 | * @return 730 | */ 731 | int main() { 732 | const string test_file = "/data/test_data.txt"; 733 | const string result_file = "/projects/student/result.txt"; 734 | #ifdef TEST 735 | auto start = std::chrono::steady_clock::now(); 736 | #endif 737 | ReadData(test_file); 738 | BuildGraph(); 739 | TopoSort(); 740 | if(g_max_edge < 2000) 741 | Use16ForDst::AllocTask(); 742 | else if(g_sum_edge < INT32_MAX) 743 | Use32ForDst::AllocTask(); 744 | else 745 | Use64ForDst::AllocTask(); 746 | Write(result_file); 747 | #ifdef TEST 748 | auto end = std::chrono::steady_clock::now(); 749 | double dr_ms = std::chrono::duration(end - start).count(); 750 | cout << "[total cost time]: " << dr_ms << " ms\n"; 751 | #endif 752 | return 0; 753 | } 754 | -------------------------------------------------------------------------------- /third_round_update.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 队伍:一方通行 3 | * 队员:宋星辰 4 | * 邢贝东 5 | * 张赛 6 | * 口号:以凡人之力上达天意者,一方通行是也 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | using namespace std; 14 | 15 | //#define TEST 16 | 17 | #define Inline __inline__ __attribute__((always_inline)) 18 | 19 | typedef uint8_t ui8; 20 | typedef uint16_t ui16; 21 | typedef uint32_t ui32; 22 | typedef uint64_t ui64; 23 | 24 | /** 25 | * 定义:线程数目 26 | * 映射后节点数目 27 | * 反哈希数组 28 | * 多线程读取文件的必要信息 29 | */ 30 | const ui8 THREAD_NUM = 12; 31 | ui32 g_node_cnt; 32 | char* g_buf; 33 | struct Bi{ 34 | ui32 tid; 35 | ui32 l; 36 | ui32 r; 37 | }; 38 | vector g_unhash_id; 39 | vector g_node_data[THREAD_NUM]; 40 | vector g_graph_data[THREAD_NUM]; 41 | 42 | 43 | /** 44 | * 整型转换为字符型函数 45 | * @param s 46 | * @return 47 | */ 48 | Inline ui32 MyAtoi(char* s){ 49 | ui32 ans = 0; 50 | while(47 < *s && *s < 58){ 51 | ans *= 10; 52 | ans += (*s - 48); 53 | s++; 54 | } 55 | return ans; 56 | } 57 | 58 | /** 59 | * 多线程读取数据的工作函数 60 | * @param bi 61 | */ 62 | void ReadJob(Bi& bi){ 63 | char* bg = g_buf + bi.l; 64 | const char* ed = g_buf + bi.r; 65 | char tmp[16]; 66 | g_node_data[bi.tid].reserve(1000000); 67 | g_graph_data[bi.tid].reserve(500000); 68 | ui32 line[3]; 69 | while(bg < ed){ 70 | for(ui32 & ii : line){ 71 | ui8 index = 0; 72 | while(*(bg+index) != ',' && *(bg+index) != '\n') index++; 73 | memset(tmp, ' ', 16); 74 | memcpy(tmp, bg, 16); 75 | ii = MyAtoi(tmp); 76 | bg += (index + 1); 77 | } 78 | // 去掉金额为0转账记录 79 | if(line[2] != 0){ 80 | g_node_data[bi.tid].emplace_back(line[0]); 81 | g_node_data[bi.tid].emplace_back(line[1]); 82 | g_graph_data[bi.tid].emplace_back(line[2]); 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * 多线程读取数据的任务分配函数 89 | * @param file_name 90 | */ 91 | void ReadData(const string& file_name){ 92 | #ifdef TEST 93 | auto start = std::chrono::steady_clock::now(); 94 | #endif 95 | int fd = open(file_name.c_str(), O_RDONLY); 96 | ui32 length = lseek(fd, 0, SEEK_END); 97 | g_buf = (char*)mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); 98 | thread threads[THREAD_NUM]; 99 | Bi bi[THREAD_NUM]; 100 | ui32 pre_p = 0; 101 | for(ui8 i = 0; i < THREAD_NUM; ++i){ 102 | ui32 p = (i + 1) * length / THREAD_NUM; 103 | while (p > 0 && g_buf[p - 1] != '\n') --p; 104 | bi[i].l = pre_p; 105 | bi[i].r = p; 106 | pre_p = p; 107 | bi[i].tid = i; 108 | threads[i] = thread(ReadJob, ref(bi[i])); 109 | } 110 | for(auto & thread : threads){ 111 | thread.join(); 112 | } 113 | #ifdef TEST 114 | auto end = std::chrono::steady_clock::now(); 115 | double dr_ms = std::chrono::duration(end-start).count(); 116 | cout << "[read test data time]: " << dr_ms << " ms\n"; 117 | #endif 118 | } 119 | 120 | 121 | /** 122 | * 定义:最大点数目,最大边数目 123 | * 点的出度,出度开始位置 124 | * 点的入度,入度开始位置 125 | * 前向星数据结构 126 | */ 127 | const ui32 MAX_NODE = 2500000; 128 | const ui32 MAX_EDGE = 2500000; 129 | ui32 g_max_edge; 130 | ui64 g_sum_edge; 131 | ui16 g_ind[MAX_NODE]; 132 | ui16 g_outd[MAX_NODE]; 133 | ui32 g_ind_bg[MAX_NODE]; 134 | ui32 g_head[MAX_NODE]; 135 | struct Edge { 136 | ui32 to; 137 | ui32 money; 138 | }; 139 | Edge g_graph[MAX_EDGE]; 140 | 141 | 142 | /** 143 | * 建图函数:合并线程读取的数据,排序、去重 144 | * 获得映射后节点数目,反哈希数组 145 | * 哈希原始节点 146 | * 使用前向星建图 147 | */ 148 | void BuildGraph(){ 149 | #ifdef TEST 150 | auto start = std::chrono::steady_clock::now(); 151 | #endif 152 | // 数据处理 153 | vector node_d; 154 | for(auto & it : g_node_data){ 155 | node_d.insert(node_d.end(), it.begin(), it.end()); 156 | } 157 | vector tmp = node_d; 158 | sort(tmp.begin(), tmp.end()); 159 | tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end()); 160 | g_node_cnt = tmp.size(); 161 | g_unhash_id = tmp; 162 | unordered_map hash_id; 163 | for(ui32 i = 0; i < g_node_cnt; ++i) 164 | hash_id[tmp[i]] = i; 165 | // 开始建图 166 | ui32* curlen = new ui32[g_node_cnt + 1](); 167 | for(auto & it : g_node_data){ 168 | for(ui32 j = 0; j < it.size(); j+=2){ 169 | g_outd[hash_id[it[j]]]++; 170 | } 171 | } 172 | g_head[0] = 0; 173 | for(ui32 i = 1; i <= g_node_cnt; ++i){ 174 | g_head[i] = g_head[i - 1] + g_outd[i - 1]; 175 | } 176 | ui32 from, to, money; 177 | for(ui8 i = 0; i < THREAD_NUM; ++i){ 178 | for(ui32 j = 0, k = 0; j < g_node_data[i].size(); j+=2, ++k){ 179 | from = hash_id[g_node_data[i][j]]; 180 | to = hash_id[g_node_data[i][j+1]]; 181 | money = g_graph_data[i][k]; 182 | g_graph[g_head[from] + curlen[from]].to = to; 183 | g_graph[g_head[from] + curlen[from]++].money = money; 184 | g_ind[to]++; 185 | g_max_edge = g_max_edge < money ? money : g_max_edge; 186 | g_sum_edge += money; 187 | } 188 | } 189 | g_ind_bg[0] = 0; 190 | for(ui32 i = 1; i < g_node_cnt; ++i){ 191 | g_ind_bg[i] = g_ind_bg[i - 1] + g_ind[i-1]; 192 | } 193 | #ifdef TEST 194 | auto end = std::chrono::steady_clock::now(); 195 | double dr_ms = std::chrono::duration(end-start).count(); 196 | cout << "[build graph time]: " << dr_ms << " ms\n"; 197 | #endif 198 | } 199 | 200 | 201 | /** 202 | * 定义:不使用Dijk算法的节点标记 203 | * 前驱链中出度为1的链路数目总和 204 | * 前驱数组:当“前驱点”的“前驱链中出度为1的链路数目总和”大于0时,记录 205 | */ 206 | bool g_jump_over[MAX_NODE]; 207 | ui32 g_pre_single_outd_num[MAX_NODE]; 208 | vector g_pre_node[MAX_NODE]; 209 | 210 | 211 | /** 212 | * 拓扑排序函数: 213 | * 当拓扑到的点出度大于1时,直接拓扑排序即可 214 | * 当拓扑到的点出度为1时,标记其不使用Dijk算法,并酌情记录其他信息,继续拓扑排序 215 | */ 216 | void TopoSort(){ 217 | #ifdef TEST 218 | auto start = std::chrono::steady_clock::now(); 219 | #endif 220 | queue q; 221 | for(int i = 0; i < g_node_cnt; ++i){ 222 | if(0 == g_ind[i]) q.push(i); 223 | } 224 | while(!q.empty()){ 225 | int v = q.front(); 226 | q.pop(); 227 | if(g_outd[v] > 1){ 228 | const ui32 &l = g_head[v], &r = g_head[v+1]; 229 | Edge* e = &g_graph[l]; 230 | for(ui32 k = l; k < r; ++k, ++e) if(0 == --g_ind[e->to]) q.push(e->to); 231 | } 232 | else if(g_outd[v] == 1){ 233 | g_jump_over[v] = true; 234 | const ui32 &adj_node = g_graph[g_head[v]].to; 235 | g_pre_single_outd_num[adj_node] += (g_pre_single_outd_num[v] + 1); 236 | if(g_pre_single_outd_num[v] > 0) g_pre_node[adj_node].emplace_back(v); 237 | if(0 == --g_ind[adj_node]) q.push(adj_node); 238 | } 239 | } 240 | #ifdef TEST 241 | auto end = std::chrono::steady_clock::now(); 242 | double dr_ms = std::chrono::duration(end-start).count(); 243 | cout << "[topo sort time]: " << dr_ms << " ms\n"; 244 | #endif 245 | } 246 | 247 | 248 | /** 249 | * 定义:[速度测试]总处理节点数目--原子量 250 | * 线程抢占的节点--原子量 251 | * 线程的关键中心性数组 252 | */ 253 | #ifdef TEST 254 | atomic cnt(0); 255 | #endif 256 | atomic g_th_node(0); 257 | double* g_bc[THREAD_NUM]; 258 | 259 | 260 | /** 261 | * 更新没有使用Dijk算法的点的关键中心性 262 | * 计算公式:对于当前点的前驱数组中记录的点u:g_bc[u] += g_pre_single_outd_num[u] * new_delta; 263 | * @param s 264 | * @param delta 265 | * @param tid 266 | */ 267 | void UpdataJumpOverNode(const ui32 &s, const double &new_delta, const ui8 &tid){ 268 | for(ui32 & u : g_pre_node[s]){ 269 | g_bc[tid][u] += g_pre_single_outd_num[u] * new_delta; 270 | UpdataJumpOverNode(u, new_delta + 1, tid); 271 | } 272 | } 273 | 274 | 275 | /** 276 | * class Sol: 277 | * 改用了模板编程:T->ui16/ui32/ui64 278 | */ 279 | template class Sol{ 280 | public: 281 | struct Heap{ 282 | ui32 num; 283 | T dist; 284 | bool operator < (const Heap &t) const { 285 | return dist > t.dist; 286 | } 287 | }; 288 | 289 | public: 290 | static void DijkWithHeap(const ui32& s, const ui8& tid, ui32* pred, ui16* len_of_pred, T* dst, ui16* sigma, double* delta, ui32* lst, bool* visit, Heap* heap) { 291 | int len_of_lst = 0; 292 | sigma[s] = 1; 293 | 294 | // Dijk算法 + 堆优化 295 | Heap tmp{}; 296 | tmp.num = s; 297 | tmp.dist = 0; 298 | heap[0] = tmp; 299 | ui32 heap_sz = 1; 300 | while(heap_sz) { 301 | pop_heap(heap, heap + heap_sz--); 302 | if(visit[heap[heap_sz].num]) continue; 303 | Heap u = heap[heap_sz]; 304 | const ui32 &v = u.num; 305 | visit[v] = true; 306 | lst[len_of_lst++] = v; 307 | const ui32 &l = g_head[v], &r = g_head[v+1]; 308 | Edge* e = &g_graph[l]; 309 | for(ui32 k = l; k < r; ++k, ++e){ 310 | const ui32 &adj_node = e->to; 311 | const T &new_dist = u.dist + e->money; 312 | if(new_dist > dst[adj_node]) continue; 313 | if(!visit[adj_node] && new_dist < dst[adj_node]){ 314 | dst[adj_node] = new_dist; 315 | tmp.num = adj_node; 316 | tmp.dist = new_dist; 317 | heap[heap_sz++] = tmp; 318 | push_heap(heap, heap + heap_sz); 319 | len_of_pred[adj_node] = 0; 320 | pred[g_ind_bg[adj_node] + len_of_pred[adj_node]++] = v; 321 | sigma[adj_node] = sigma[v]; 322 | } 323 | else if(new_dist == dst[adj_node]) { 324 | pred[g_ind_bg[adj_node] + len_of_pred[adj_node]++] = v; 325 | sigma[adj_node] += sigma[v]; 326 | } 327 | } 328 | } 329 | 330 | // 更新本轮Dijk的关键中心性贡献值 331 | for(int i = len_of_lst - 1; i > 0; --i) { 332 | const ui32 &v = lst[i]; 333 | for(ui32 j = 0; j < len_of_pred[v]; ++j){ 334 | delta[pred[g_ind_bg[v] + j]] += (1.0 + delta[v]) * sigma[pred[g_ind_bg[v] + j]] / sigma[v]; 335 | } 336 | g_bc[tid][v] += ((g_pre_single_outd_num[s] + 1) * delta[v]); 337 | } 338 | g_bc[tid][s] += g_pre_single_outd_num[s] * delta[s]; 339 | if(!g_pre_node[s].empty()) UpdataJumpOverNode(s, delta[s] + 1, tid); 340 | 341 | // 为下一次Dijk初始化 342 | for(ui32 i = 0; i < len_of_lst; ++i) { 343 | const ui32 &v = lst[i]; 344 | dst[v] = INT64_MAX; 345 | sigma[v] = 0; 346 | delta[v] = 0.0; 347 | visit[v] = false; 348 | } 349 | } 350 | 351 | /** 352 | * 多线程Dijk的工作函数 353 | * 使用原子量抢占获得Dijk的起始点 354 | * [测试]每处理1000个点,打印一下 355 | * @param tid 356 | */ 357 | static void GetBc(const ui8 tid) { 358 | bool* visit = new bool[g_node_cnt](); 359 | ui16* sigma = new ui16[g_node_cnt](); 360 | ui16* len_of_pred = new ui16[g_node_cnt]; 361 | ui32* pred = new ui32[MAX_EDGE]; 362 | ui32* lst = new ui32[g_node_cnt]; 363 | T* dst = new T[g_node_cnt]; 364 | auto* delta = new double[g_node_cnt](); 365 | Heap* heap = new Heap[MAX_NODE]; 366 | for(ui32 i = 0; i < g_node_cnt; ++i){ 367 | dst[i] = INT64_MAX; 368 | } 369 | while(true){ 370 | ui32 node = g_th_node++; 371 | if(node >= g_node_cnt) break; 372 | if(g_jump_over[node]) continue; 373 | DijkWithHeap(node, tid, pred, len_of_pred, dst, sigma, delta, lst, visit, heap); 374 | #ifdef TEST 375 | ui32 t = ++cnt; 376 | if(t % 1000 == 0 || t == g_node_cnt) printf("%d/%d\n", t, g_node_cnt); 377 | #endif 378 | } 379 | delete [] visit; 380 | delete [] sigma; 381 | delete [] len_of_pred; 382 | delete [] pred; 383 | delete [] lst; 384 | delete [] dst; 385 | delete [] delta; 386 | delete [] heap; 387 | } 388 | 389 | /** 390 | * 多线程Dijk任务分配函数 391 | */ 392 | static void AllocTask(){ 393 | #ifdef TEST 394 | auto start = std::chrono::steady_clock::now(); 395 | #endif 396 | thread threads[THREAD_NUM]; 397 | for(ui8 i = 0; i < THREAD_NUM; ++i){ 398 | g_bc[i] = new double[g_node_cnt](); 399 | threads[i] = thread(Sol::GetBc, i); 400 | } 401 | for(auto & thread : threads){ 402 | thread.join(); 403 | } 404 | #ifdef TEST 405 | auto end = std::chrono::steady_clock::now(); 406 | double dr_ms = std::chrono::duration(end - start).count(); 407 | cout << "[calculate time]: " << dr_ms << " ms\n"; 408 | #endif 409 | } 410 | }; 411 | 412 | 413 | /** 414 | * 精度控制函数:用于std::sort 415 | * @param a 416 | * @param b 417 | * @return 418 | */ 419 | Inline bool PrecisionCmp(const pair &a, const pair &b){ 420 | if(abs(a.first - b.first) > 0.0001) return a.first > b.first; 421 | else return a.second < b.second; 422 | } 423 | 424 | /** 425 | * 输出函数:汇总多线程的关键中心性,排序,取top100输出 426 | * @param result_file 427 | */ 428 | void Write(const string &result_file){ 429 | auto* bc_final = new double[g_node_cnt](); 430 | for(auto & ii : g_bc){ 431 | for(ui32 j = 0; j < g_node_cnt; ++j){ 432 | bc_final[j] += ii[j]; 433 | } 434 | } 435 | auto res = new pair[g_node_cnt]; 436 | for(ui32 i = 0; i < g_node_cnt; ++i){ 437 | res[i] = make_pair(bc_final[i], i); 438 | } 439 | sort(res, res + g_node_cnt, PrecisionCmp); 440 | ui8 index = g_node_cnt < 100 ? g_node_cnt : 100; 441 | ofstream write(result_file.c_str(), ios::out); 442 | for(ui8 i = 0; i < index; ++i){ 443 | write << g_unhash_id[res[i].second] << ',' << fixed << setprecision(3) << res[i].first << '\n'; 444 | } 445 | write.close(); 446 | delete [] bc_final; 447 | delete [] res; 448 | } 449 | 450 | 451 | /** 452 | * 主函数依次执行: 453 | * 1、读取函数 454 | * 2、建图函数 455 | * 3、拓扑排序函数 456 | * 4、选择Dijk类 457 | * 5、输出函数 458 | * @return 459 | */ 460 | int main() { 461 | const string test_file = "/data/test_data.txt"; 462 | const string result_file = "/projects/student/result.txt"; 463 | #ifdef TEST 464 | auto start = std::chrono::steady_clock::now(); 465 | #endif 466 | ReadData(test_file); 467 | BuildGraph(); 468 | TopoSort(); 469 | if(g_max_edge < 2000) 470 | Sol::AllocTask(); 471 | else if(g_sum_edge < INT32_MAX) 472 | Sol::AllocTask(); 473 | else 474 | Sol::AllocTask(); 475 | Write(result_file); 476 | #ifdef TEST 477 | auto end = std::chrono::steady_clock::now(); 478 | double dr_ms = std::chrono::duration(end - start).count(); 479 | cout << "[total cost time]: " << dr_ms << " ms\n"; 480 | #endif 481 | return 0; 482 | } 483 | 484 | 485 | --------------------------------------------------------------------------------