华为codecraft算法大赛---寻路
来源:互联网 发布:立秋网络怎么获得学时 编辑:程序博客网 时间:2024/05/16 01:04
华为codecraft算法大赛—寻路
前言
最近实验室的师兄师姐们在热火朝天的笔试(都说难难难),我也要了些题来感受了一下,已然被虐的体无完肤。选择题考的内容涉及范围广,算法编程题对于没有刷题经验的我来说就更是难上加难了。看来有必要在学习工作之余学习学习算法以及计算机基础知识了。
翻了上半年参加华为codecraft算法大赛的代码,趁周末整理一下当时的思路以及回顾一下数据结构与算法。比赛前中期还保持不错的名次,一直维持在二十名左右以为稳稳三十二强了,结果后期突破不大,最后几天呼呼的被超过,真是太天真了。后来回想确实方法思路比较死板,一口咬定DFS+剪枝,没有去尝试新的算法如A*,模拟退火算法等,还有大赛群里分享说的动态规划、数模等。
比赛源码以及样例数据在我的github:
https://github.com/hczheng/2016Codecraft
赛题介绍
1 问题定义
给定一个带权重的有向图G=(V,E),V为顶点集,E为有向边集,每一条有向边均有一个权重。对于给定的顶点s、t,以及V的子集V’,寻找从s到t的不成环有向路径P,使得P经过V’中所有的顶点(对经过V’中节点的顺序不做要求)。
若不存在这样的有向路径P,则输出无解,程序运行时间越短,则视为结果越优;若存在这样的有向路径P,则输出所得到的路径,路径的权重越小,则视为结果越优,在输出路径权重一样的前提下,程序运行时间越短,则视为结果越优。
说明:
1)图中所有权重均为[1,20]内的整数;
2)任一有向边的起点不等于终点;
3)连接顶点A至顶点B的有向边可能超过一条,其权重可能一样,也可能不一样;
4)该有向图的顶点不会超过600个,每个顶点出度(以该点为起点的有向边的数量)不超过8;
5)V’中元素个数不超过50;
6)从s到t的不成环有向路径P是指,P为由一系列有向边组成的从s至t的有向连通路径,且不允许重复经过任一节点;
7)路径的权重是指所有组成该路径的所有有向边的权重之和。
2 输入与输出
输入文件格式
以两个.csv 文件(csv 是以逗号为分隔符的文本文件)给出输入数据,一个为图的数据(G),一个为需要计算的路径信息(s,t,V’)。文件每行以换行符(ASCII’\n’即0x0a)为结尾。
1)图的数据中,每一行包含如下的信息:
LinkID,SourceID,DestinationID,Cost
其中,LinkID 为该有向边的索引,SourceID 为该有向边的起始顶点的索引,DestinationID为该有向边的终止顶点的索引,Cost 为该有向边的权重。顶点与有向边的索引均从0 开始 编号(不一定连续,但用例保证索引不重复)。
2)路径信息中,只有一行如下数据:
SourceID,DestinationID,IncludingSet
其中,SourceID 为该路径的起点,DestinationID 为该路径的终点,IncludingSet 表示必须经过的顶点集合V’,其中不同的顶点索引之间用’|’分割。
输出文件格式
输出文件同样为一个.csv 文件。
1)如果该测试用例存在满足要求的有向路径P,则按P 经过的有向边顺序,依次输出有向边的索引,索引之间用’|’分割;
2)如果该测试用例不存在满足要求的有向路径P,则输出两个字符NA;
3)只允许输出最多一条有向路径。
3.简单用例说明
赛题思路
我当时第一反应就是最短路径算法,但是直接套用过来是肯定不行的,因为最短路径算法(dijkstra)是一层一层往外扩,然后利用贪心算法的思想得到每一层级的最优路径。二本赛题加了必过点的限制,因此需要对算法进行改造,为了能得到赛题结果,在一步步改造的过程中发现已经抛弃了贪心算法思想,最后渐渐的升级深度优先遍历(DFS)的基本思想,但是记录每一步遍历的顺序,方便回溯(在找到可行解的基础上还要考虑最优解)。随着比赛的进行,赛题数据复杂度的增加,回溯版DFS不能在有效的时间内得到最优解,甚至不能得到一个可行解。因此我又在该基础上增加了”剪枝”的思想,就是剪断一些出度较大的节点的路径,这样可以降低复杂度,并且能得到不错的结果,“剪枝”的尺度与位置其实是随机(缺乏科学性)的,由于是比赛,所以可以通过评分程序去修改算法拟合赛题数据。也是因为这种小聪明导致我这次比赛前中期效果不错,后期却非常乏力。这是应该吸取的教训,还是应该从算法本身的有效性入手,那样才会有真正的提高。
以下是输入数据以及输出结果样例
比赛源码
比赛要求的编译环境是Linux+gcc;为了方便调试,以下代码(已带详细注释)是Windows+Visual Stdio下的,有兴趣可以自行转换,也可以在以下地址下载提交版(包括华为官方提供的编译脚本)。
#include "stdafx.h"#include <stdio.h>#include <string.h>#include <stdlib.h>#include <fstream> using namespace std;#define PRINT printf#define MAX_RECORD 100#define MAX_LINE_LEN 4000#define Error( Str ) FatalError( Str )#define FatalError( Str ) fprintf( stderr, "%s\n", Str ), exit( 1 )//邻接表读图,//前面这些把图读进邻接表,typedef struct ListNode *Position;//位置typedef Position List;//链表typedef struct Tbl *Table;//邻接表typedef int ElementType;typedef int Vertex;//顶点typedef struct VertexTable *VerTable;//例子4个顶点#define Infinity 65536 //各个顶点初始化#define NotAVertex -1#define nowstart 0//当前起点,初始化为0#define NotFound -1//折半查找#define LeftChild(i) (2*(i)+1)//堆排序 typedef struct StackRecord *Stack;#define EmptyTOS -1//*************************************读文件********************************************//int read_file(char ** const buff, const unsigned int spec, const char * const filename){ FILE *fp = fopen(filename, "r"); if (fp == NULL) { PRINT("Fail to open file %s, %s.\n", filename, strerror(errno)); return 0; } PRINT("Open file %s OK.\n", filename); char line[MAX_LINE_LEN + 2]; unsigned int cnt = 0; while ((cnt < spec) && !feof(fp)) { line[0] = 0; fgets(line, MAX_LINE_LEN + 2, fp); if (line[0] == 0) continue; buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2); strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1); buff[cnt][4001] = 0; cnt++; } fclose(fp); PRINT("There are %d lines in file %s.\n", cnt, filename); return cnt;}//*************************************基本数据结构********************************************//struct StackRecord{ int Capacity; int TopOfStack; ElementType *Array;};////创建栈Stack CreateStack(int MaxElements){ Stack S; S = (struct StackRecord*)malloc(sizeof(struct StackRecord)); if (S == NULL) FatalError("Out of space!!!"); S->Array = (int*)malloc(sizeof(ElementType)*MaxElements); if (S->Array == NULL) FatalError("Out of space!!!"); S->Capacity = MaxElements; S->TopOfStack = EmptyTOS; return S;}//出栈void Pop(Stack S){ if (S->TopOfStack == EmptyTOS) Error("Empty Stack"); else S->TopOfStack--;}//入栈void Push(ElementType X, Stack S){ if (S->TopOfStack == S->Capacity - 1) Error("Full stack"); else S->Array[++S->TopOfStack] = X;}//销毁栈void DisposeStack(Stack S){ if (S != NULL) { free(S->Array); free(S); }}ElementType Top(Stack S){ if (S->TopOfStack != EmptyTOS) { return S->Array[S->TopOfStack]; } else { Error("Empty Stack"); return 0; }}//链表,每个存储了顶点Element,权重Cost,指向下一个的Next;struct ListNode{ ElementType ELement; int Cost; int Priority; int Edge; Position Next;};//0->1,1 Element=1,cost=1; //邻接表struct Tbl{ int TableSize; List *TheLists;};//顶点表struct VertexTable{ int known; int Dist; Vertex Path;};//*************************************邻接表初始化******************************************//Table InitializeTable(int TableSize){ Table T; int i; T = (struct Tbl*)malloc(sizeof(struct Tbl)); if (T == NULL) FatalError("Out of space!!!"); T->TableSize = TableSize; T->TheLists = (struct ListNode**)malloc(sizeof(List)*T->TableSize); for (i = 0;i<T->TableSize;i++) { T->TheLists[i] = (struct ListNode*)malloc(sizeof(struct ListNode)); if (T->TheLists[i] == NULL) FatalError("Out of space!!!"); else T->TheLists[i]->Next = NULL; } return T;}//*************************************链表插入********************************************//void Insert(int Edge, ElementType Pos, ElementType Key, ElementType Cost, ElementType Priority, Table T){ Position NewCell; List L; NewCell = (struct ListNode*)malloc(sizeof(struct ListNode)); if (NewCell == NULL) FatalError("Out of space!!!"); else { L = T->TheLists[Pos]; NewCell->Next = L->Next; NewCell->Edge = Edge; NewCell->ELement = Key; NewCell->Cost = Cost; NewCell->Priority = Priority; L->Next = NewCell; }}//*************************************顶点表初始化*****************************************//VerTable InitialWeighted(Vertex Start, VerTable V, int NumVertex)//V为顶点集合,每隔顶点有三种标记{ int i; V = (struct VertexTable*)malloc(sizeof(struct VertexTable)*NumVertex); for (i = 0;i<NumVertex;i++) { V[i].known = 0; V[i].Dist = 0; V[i].Path = NotAVertex; } V[Start].Dist = 0; return V;}//*************************************二分查找********************************************//int BinarySearch(const ElementType A[], ElementType X, int N){ int Low, Mid, High; Low = 0;High = N - 1; while (Low <= High) { Mid = (Low + High) / 2; if (A[Mid]<X) Low = Mid + 1; else if (A[Mid]>X) High = Mid - 1; else return Mid; } return NotFound;}void MakeEmpty(List L){ Position P, Tmp; P = L->Next; L->Next = NULL; while (P != NULL) { Tmp = P->Next; free(P); P = Tmp; }}void Disposable(Table T){ int i; for (i = 0;i<T->TableSize;i++) { MakeEmpty(T->TheLists[i]); } free(T->TheLists); free(T);}//*************************************堆排序********************************************//void PercDown(ElementType A[], int i, int N){ int Child; ElementType Tmp; for (Tmp = A[i];LeftChild(i)<N;i = Child) { Child = LeftChild(i); if (Child != N - 1 && A[Child + 1]>A[Child]) Child++; if (Tmp<A[Child]) A[i] = A[Child]; else break; } A[i] = Tmp;}void Swap(int *a, int *b){ int temp; temp = *a; *a = *b; *b = temp;}void Heapsort(ElementType A[], int N){ int i; for (i = N / 2;i >= 0;i--) PercDown(A, i, N); for (i = N - 1;i>0;i--) { Swap(&A[0], &A[i]); PercDown(A, 0, i); }}//*************************************打印路径********************************************//void PrintPath(Vertex Ver, VerTable V, int C[]){ if (V[Ver].Path != NotAVertex) { PrintPath(V[Ver].Path, V, C); printf("->"); } printf("%d", C[Ver]);}//************************************输出边编号*******************************************//int find_route(int stop, Table T, VerTable V){ Position Tmp; int result = -1; Tmp = T->TheLists[V[stop].Path]->Next;// while (Tmp != NULL) { if (Tmp->ELement == stop) { result = Tmp->Edge;break; } Tmp = Tmp->Next; } return result;}///*************************************重复边处理****************************************//Position FindPrevious(ElementType X, List L){ Position P; P = L; while (P->Next != NULL && P->Next->ELement != X) P = P->Next; return P;}int IsLast(Position P, List L){ return P->Next == NULL;}void Delete(ElementType X, List L){ Position P, TmpCell; P = FindPrevious(X, L); if (!IsLast(P, L)) /* Assumption of header use */ { /* X is found; delete it */ TmpCell = P->Next; P->Next = TmpCell->Next; /* Bypass deleted cell */ free(TmpCell); }}Position Find(ElementType X, List L){ Position P; P = L->Next; while (P != NULL && P->ELement != X) P = P->Next; return P;}///*******************************/自定义查找下一顶点的算法*************************************//int find_start(VerTable V, Table T, ElementType demand[], int start_now, int known_p, int end, Stack S, int N){ //传入的参数分别为:顶点表(konwn,dis,path)、优先点集、当前遍历起点 //返回值为下一个起点索引TempV,以及特征点入栈 Position tmp; int min = Infinity;//普通点最小权值 int min_sp = Infinity;//优先点最小权值 int count_sp = 0;//优先点计数(用于判断特征点) int count_normal = 0;//普通点计数 int normal[8] = { 0 };//普通点数组(暂存) int special[8] = { 0 };//优先点数组(暂存) Vertex TempV = -1;//开始标记 int flag = 0;//终点标记 //TMP = T->TheLists[start_now]; tmp = T->TheLists[start_now]->Next; while (tmp != NULL) //0->3->1->NULL 还有邻接点且未到达过 { if (V[tmp->ELement].known != 1) { if (tmp->Priority == 1)//如果该顶点是优先点 { count_sp++;//当前层级优先点数+1 if (tmp->Cost < min_sp) //当前点权值更小 { if (count_sp > 1) { special[count_sp - 2] = TempV; V[TempV].Dist = 0; } min_sp = tmp->Cost;//更新min_sp TempV = tmp->ELement;//返回值 V[TempV].Dist = V[start_now].Dist + tmp->Cost; } else { if (count_sp > 1) { special[count_sp - 2] = tmp->ELement;; } } } else if (tmp->ELement == end)//如果该顶点是终点 { flag = 1;//表明这一轮有终点,但暂时不作处理 V[end].Dist = V[start_now].Dist + tmp->Cost; V[end].Path = start_now; } else //如果该顶点是普通点 { count_normal++;//普通点计数 normal[count_normal - 1] = tmp->ELement;//普通点暂存 if (count_sp == 0 && tmp->Cost < min)//(1.有普通点没有终点没有优先点2.有普通点有终点没有优先点) { min = tmp->Cost;//更新min V[TempV].Dist = 0; TempV = tmp->ELement;//返回值 V[TempV].Dist = V[start_now].Dist + tmp->Cost; } } } tmp = tmp->Next; } ///////////*************************LOOK***************************/////////////// if (count_sp == 0) //假如没有优先点,则把多余的普通点全部入栈 { for (int i = 0;i < count_normal;i++) { if (TempV != normal[i]) { Push(normal[i], S);Push(start_now, S);//V[normal[i]].Path = start_now; } }//普通点分支入栈 } else//当优先点数>=1时 { if (count_sp == 1)//假如刚好只有一个优先点,那么普通点入栈,并赋值path { for (int i = 0;i < count_normal;i++) { Push(normal[i], S); Push(start_now, S); }//V[normal[i]].Path = start_now; } else//当优先点数大于1时,普通点先全部入栈,然后多余的优先点入栈 { for (int i = 0;i < count_normal;i++) { Push(normal[i], S); Push(start_now, S); } //V[normal[i]].Path = start_now; for (int i = 0;i < count_sp - 1;i++) { Push(special[i], S);Push(start_now, S); }//V[special[i]].Path = start_now; } } if (flag == 1 && known_p == N)//已到终点,且这一层没有优先点了,就要判断是否已经结束(优先点已全部在路径中) { TempV = 10000; //10000结束标志,到达终点且所有的优先点已在路径中 } if (TempV == -1) { TempV = -start_now; if (start_now == 0) { TempV = -1000; } }//返回停止点(带负号,方便处理) if (TempV != 10000 && TempV >= 0) { V[TempV].Path = start_now; } return TempV;//返回下一起点索引}//**************************************核心查找算法********************************************//int DF(VerTable V, Table T, ElementType demand[], Stack S, int N, int start_now, int end, unsigned short *result,int big){ //Vertex Ver, W;//顶点索引 Position Tmp; //int end = 5;//假定一个终点 int startone=start_now ;//存起点 int known_p = 0;//记录已到过的优先点 int flag = 0;//遍历过优先点标记 int min_dist = 10000; int ok = 0; for (;;) { if (start_now == 10000)//当前起点为10000,查找成功 { flag++;//迭代次数 if (min_dist > V[end].Dist) { int tmp = end; int count = 0; while (tmp != startone) { result[count] = find_route(tmp, T, V); count++; ok = count; tmp = V[tmp].Path; } min_dist = V[end].Dist; } else V[end].Dist = min_dist; if (S->TopOfStack == -1||flag>10) break; printf("flag:%d\n",flag); //if (big>7&&big<30)break; //if (big>150 && big<200&&flag>7)break; //if (big>200 && big<250 && flag>3)break; if (big>250&&flag>0)break; int pass = Top(S);//获得特征点索引 0->1->3 Pop(S);//出栈 start_now = Top(S);//将当前起点改为栈顶元素 int stop = V[end].Path;//继续回溯 while (stop != pass) { V[stop].known = 0; if (BinarySearch(demand, stop, N) >= 0) { known_p--; } stop = V[stop].Path; } Pop(S);//出栈 /*if (S->TopOfStack != -1) { if (big> 150&&big<200) { Pop(S);Pop(S); }//边数在 if (big> 200 && big<250) { Pop(S);Pop(S);Pop(S);Pop(S); }//边数在200-250,一次剪断两条边 if (big> 250) { Pop(S);Pop(S);Pop(S);Pop(S);Pop(S);Pop(S); }//边数大于250,一次剪断三条边*/ //剪枝操作 V[start_now].Path = pass;//出栈起点路径信息 Tmp = T->TheLists[pass]->Next; while (Tmp != NULL) { if (Tmp->ELement == start_now) { V[start_now].Dist = V[pass].Dist + Tmp->Cost; } Tmp = Tmp->Next; } } if (start_now < 0 && S->TopOfStack == -1) break; //栈空,则返回无解 if (start_now <0)//回头 { int stop = -start_now;//停止点 if (start_now == -1000) { stop = 0; }//0为停止点的情况 int pass = Top(S);//获得特征点索引 0->1->3 Pop(S);//出栈 //printf("%d出栈:\n", pass); start_now = Top(S);//将当前起点改为栈顶元素 while (stop != pass) { V[stop].known = 0; if (BinarySearch(demand, stop, N) >= 0) { known_p--; } //printf("%d回撤:\n", stop); stop = V[stop].Path; //V[stop].Path = NotAVertex; } Pop(S);//出栈 //printf("%d出栈:\n", start_now); if (S->TopOfStack != -1) { //if (big>150 && big < 200) { Pop(S);Pop(S);}//边数在150-200之间,一次剪断一条边 if (big> 100&&big<250) { Pop(S);Pop(S);Pop(S);Pop(S); }//边数在200-250,一次剪断两条边 if (big> 250) { Pop(S);Pop(S);Pop(S);Pop(S);Pop(S);Pop(S);}//边数大于250,一次剪断三条边 }//剪枝操作 V[start_now].Path = pass;//出栈起点路径信息 Tmp = T->TheLists[pass]->Next; while (Tmp != NULL) { if (Tmp->ELement == start_now) { V[start_now].Dist = V[pass].Dist + Tmp->Cost; } Tmp = Tmp->Next; } } V[start_now].known = 1;//将当前起点标记为已知 if (BinarySearch(demand, start_now, N) >= 0) { known_p++; }//记录到达的优先点数 start_now = find_start(V, T, demand, start_now, known_p, end, S, N);//按自定义方法找下一个起点 printf("当前点:%d\n",start_now); } return ok;}//**************************************调用算法********************************************//void search_route(char *graph[5000], int edge_num, char *condition)//字符串数组(topo),行数,字符串(demand){ Table T = InitializeTable(600);//顶点 VerTable V = NULL; Stack S = CreateStack(1000);//创建栈(存分支点) unsigned short result[100] = { -1 };//示例中的一个解 const int n = edge_num; int N, start_now, end, demand[100] = { -1 }, test[400][4] = { 0 }; char *save_ptr, *line;//存剩余的 for (int i = 0;i<n;i++)//test初始化 { char *hello = graph[i]; char *p, *token; int j = 0; for (p = hello; ; p = NULL, j++) { token = strtok_s(p, ",", &save_ptr); if (NULL == token) break; test[i][j] = atoi(token); } } start_now = atoi(strtok_s(condition, ",", &save_ptr));//起点 printf("start_now=%d \n", start_now); end = atoi(strtok_s(NULL, ",", &save_ptr));//终点 printf("end=%d \n", end); line = strtok_s(NULL, ",", &save_ptr);//优先点字符串 printf("special_line=%s \n", line); char *get, *token; save_ptr = NULL; int d_count = 0; for (get = line; ; get = NULL, d_count++) { token = strtok_s(get, "|", &save_ptr); if (NULL == token) break; demand[d_count] = atoi(token); // printf("demand[%d]=%d \n",d_count,demand[d_count]); } N = d_count; int Sort[400 * 2] = { -1 }, Max, *C, p = 0, Stemp, Etemp, *Fdemand; for (int i = 0; i < n; i++) { Sort[2 * i] = test[i][1]; Sort[2 * i + 1] = test[i][2]; }//把起点和终点包含的点都算进来 Heapsort(Sort, n * 2);//对顶点进行排序 Max = Sort[2 * n - 1]; C = (int*)malloc(sizeof(int)*(Max + 1)); if (NULL == C) { FatalError("Out of space!!!"); } for (int i = 0;i<2 * n;i++) { if (Sort[i] != Sort[i + 1]) { C[p] = Sort[i]; p++; } } printf("edge_num=%d \n", n); printf("point_num=%d \n", p); printf("special_num=%d \n", N); Fdemand = (int*)malloc(sizeof(int)*(N));; for (int i = 0;i<N;i++) { Stemp = BinarySearch(C, demand[i], p); Fdemand[i] = Stemp; } Heapsort(Fdemand, N); Position Pos,L; /*for (int j = 0;j<n;j++)//邻接表插入 { Stemp = BinarySearch(C, test[j][1], p); Etemp = BinarySearch(C, test[j][2], p); if (BinarySearch(Fdemand, Etemp, N) >= 0) { Insert(test[j][0], Stemp, Etemp, test[j][3], 1, T);//start,stop,cost,List,把3必过点插入 T->TheLists[Stemp]->ELement++;//有几个优先级的点 } else { Insert(test[j][0], Stemp, Etemp, test[j][3], 0, T);//start,stop,cost,List,把3必过点插入 T->TheLists[Stemp]->ELement++;//有几个优先级的点 } }*///////////////////////////////////////////////////// for (int j = 0;j<n;j++) { Stemp = BinarySearch(C, test[j][1], p); Etemp = BinarySearch(C, test[j][2], p); if (BinarySearch(Fdemand, Etemp, N) >= 0) { L = T->TheLists[Stemp]; Pos = Find(Etemp, L); if (NULL == Pos) { Insert(test[j][0], Stemp, Etemp, test[j][3], 1, T);//start,stop,cost,List,把3必过点插入 T->TheLists[Stemp]->ELement++;//有几个优先级的点 } else { if (Pos->Cost > test[j][3]) { Delete(Pos->ELement, L); T->TheLists[Stemp]->ELement--;//有几个优先级的点 Insert(test[j][0], Stemp, Etemp, test[j][3], 1, T); T->TheLists[Stemp]->ELement++;//有几个优先级的点 } } } else { L = T->TheLists[Stemp]; Pos = Find(Etemp, L); if (NULL == Pos) { Insert(test[j][0], Stemp, Etemp, test[j][3], 0, T); T->TheLists[Stemp]->ELement++;//有几个优先级的点 } else { if (Pos->Cost > test[j][3]) { Delete(Pos->ELement, L); T->TheLists[Stemp]->ELement--;//有几个优先级的点 Insert(test[j][0], Stemp, Etemp, test[j][3], 0, T);//start,stop,cost,List,把3必过点插入 T->TheLists[Stemp]->ELement++;//有几个优先级的点 } } } }//////////////////////////////////////////////////// start_now = BinarySearch(C, start_now, p); end = BinarySearch(C, end, p); V = InitialWeighted(0, V, n); int route = DF(V, T, Fdemand, S, N, start_now, end, result,n);//最短路径D算法 int tmp = end; int result_count = route;//解的个数 if (route == 0) { printf("NA\n"); } else { /*if (N>=16) { result_count =0; while (tmp != start_now) { result[result_count] = find_route(tmp, T, V); result_count++; tmp = V[tmp].Path; } }*/ PrintPath(end, V, C); printf(" COST=%4d\n", V[end].Dist); for (int i = result_count - 1;i >= 0;i--) { printf("%d | ", result[i]); //record_result(result[i]); } printf("\n"); } Disposable(T); DisposeStack(S); free(V); free(C); free(Fdemand); //record_result(result[i]);}int main(){ char *topo[5000]; int edge_num; char *demand; int demand_num; edge_num = read_file(topo, 5000, "topo.csv"); demand_num = read_file(&demand, 1, "demand.csv"); search_route(topo, edge_num, demand); getchar(); return 0;}
- 华为codecraft算法大赛---寻路
- 2017华为CodeCraft回顾
- 记一次逗逼的codecraft算法大赛的参赛经历
- 华为编程大赛-排序算法
- 2017华为算法大赛总结
- 2017华为codecraft 《大视频时代布局》
- 华为编程大赛-菜鸟学算法
- 华为编程大赛
- 华为编程大赛2012
- 2010华为编程大赛
- 华为编程大赛-字符串匹配
- 华为编程大赛决赛题
- 2012 华为编程大赛 语法分析
- 华为编程大赛--路径查找
- 华为编程大赛--出圈问题
- 华为编程大赛--字符串匹配
- 华为编程大赛--高精度加减法
- 华为编程大赛汇总----java
- 电脑花屏死机怎么办?
- Python下的自然语言处理利器-LTP语言技术平台 pyltp 学习手札
- H5游戏项目开发总结
- 最新Windows 10正式版密钥,Win10激活序列号KEY大全
- HDU 5748 Bellovin
- 华为codecraft算法大赛---寻路
- 进实验室之后
- linux中的死锁
- 三角函数相关证明
- 377. Combination Sum IV
- jQuery Ajax异步处理Json数据详解
- <input>和<button>设置同样高度却不能等高
- Java加密技术
- 360WIFI2无线网卡移植 -MT7601U移植