华为2020软件精英挑战赛(有向图找环)比赛记录/代码开源

成绩

初赛0.3384武长赛区rank16,复赛A榜10.x,复赛B榜0分,结果不太好,但是学到了很多东西认识了很多大佬!
虽然昨天复赛B榜三发WA圆满结束比赛,但是还是想记录一下毕竟在这比赛上花了一个多月时间还通了几个宵。

正文

初赛

首先感谢各位大佬在比赛过程中提供的帮助(尤其是IdeaBread大佬)!其次吐槽一下出题人吧(网上吐槽的太多了我就不展开了)!
其实原本最开始是准备刷刷题准备一下实习然后找工作的,结果同学发来了这个比赛链接问要不要参加一下,想了想觉得自己的简历太水了,那就参加一下吧,当个校园经历,混个64强也算是美滋滋(大佬可以忽略…)。
其实最开始连建图都不太会(没刷题没怎么接触过),临时去补一下图的数据结构(邻接矩阵邻接表),试着用邻接矩阵写了最初的一个dfs版本(只是为了跑通),后来感觉这个矩阵需要的空间太大了,不合适。后来偶尔在知乎发现了IdeaBread大佬的baseline,看到了用vector存图的方法(vector>),感觉瞬间打开了新世界的大门!!
有了这个存图的数据结构,就可以专心改算法了,最初的版本算法很naive,所有的点都去递归遍历他的所有出度,最终求出所要的结果,再进行字典排序最后输出,后来也是看到有大佬说根本不需要排序,又看到IdeaBread大佬的方法,一个剪枝,效果奇好,对ID排序后从序号小的ID去往大的ID搜索,这样在路径中就不用再添加比当前头结点更小的路径,省了很大一部分时间!然后就是不停地优化算法,能想到的方法基本都用上了(当然由于初赛数据量比较小,所有数据结构基本都用了数组),最终拿到了0.3x的成绩,其实初赛结束前几天想到了一种很好的方法,理论上可以大幅缩短dfs的时间(实测也是大幅缩短),但是一直线下全对线上0%WA(测过了线下至少30组民间数据全对,当时甚至都开始怀疑自己之前写的代码有没有错误),那两天几乎快弃赛了,后来初赛结束前倒数第二天又想到一个方法,于是连肝两个通宵,终于给算法改出来也拿到了0.4x的成绩(努力也算没有白费),最后一天晚上队友改出来了之前想的那种方法(xjb改),抱着最后一丝希望,那天晚上最后一次提交,最后一发 编译错误 属实给我整笑了,后来查代码发现按ctrl+s的时候有一下没按到ctrl代码里面多出了一个s…
初赛结束前夜,赛区蹦出来无数小号,我们就也开了一个小号传了一版,0.4x,比之前优化过的另一种方法效果还好,于是又肝了一个通宵优化…最终看到赛题组的5W不算违规,也加了上去,最后在下午三点多改完最终版本提交,拿到了0.3x的成绩,初赛结束,从100+名直到删完小号后的20名之内,感觉真的是在坐过山车。
最后一发代码

复赛

复赛A榜赛题很简单,加上金额判断,但是数据量比初赛多了几个量级(可能考虑到初赛被选手吊打,复赛加强一下,虽然也没见加强了个啥),稍微改了一下代码就能过了,但是数据量上去了数组肯定是不行了,于是先改了一个vector的版本提交,确认算法没有错误,得分也是惨不忍睹。
后面没办法了vector效率太低了呀虽然好用,咋办呢。没办法,手撸自己的stl吧(写的很搓,其实就是一个动态扩容的数组),其实在这段手撸的时间中学到了不少的东西,也因为这个比赛看了不少stl底层实现的文章,为了提高效率。手撸出来后其实效果也还不错,但是和前排大佬差距还是有点远呀,想着换回之前的算法,但是不敢开数组了(赛题组说复赛会换数据集)怕最后RE(没想到其实是WA,。所以那个非常快的方法也不得已而弃之,只好另寻他法。
中间优化到A榜10.x分(8线程)感觉已经没有什么点子了,和前排大佬差了许多,也用sleep测了一下时间,感觉没太大希望冲前四而且实在是时间紧张(实验室天天催着做任务)只好挂机了。中间有时间就调调多线程的参数(太菜了不会写负载均衡),后来在和同学的交流过程中想到了一种比较好的均衡方法,但是这种方法需要配合多线程输出(恰好我不太会IO,读入mmap都是同学写的)所以也只好放弃(说到底还是自己太菜了,如果能写出来感觉分能提高不少)。然后就几乎一直挂机到复赛最后一天。
最后一天本来准备交一版最稳的代码先拿个分再看能不能有什么trick优化一手,没想到正在吃串看赛题直播的时候突然看到换了两个需求(8环+浮点数),其实我的内心是有点慌的,8环倒还好,对于我这种菜鸡来说8环无非是增加了dfs的时间,不会影响结果,但是浮点数就有点难搞了。
于是我放下了手中的串串,立即赶回家打开TX会议和同学们交流一波,改8环其实只花了5分钟不到就改完了,后面就一直卡在浮点数的精度问题上(实际上还是因为我太菜了)。队友改的mmap读取浮点数也一直有问题(其实是一个小错误我们都没有发现),因为有五次提交机会我就先改了一版scanf读取double交了,然后就出现了第一发%0WA(心态有点小炸,在群里和大佬们的答案都对的上),猜测应该不会是找环的问题,很大可能是卡在浮点数精度的问题上了(事实证明也是如此),直接读取double然后*100转换为long long类型处理,也想了方法来解决这个精度问题,可是还是太菜了并没有改成功(如果一开始就集中力量改mmap可能会成功),最后以三发WA结束了比赛(还有两版因为上传时间间隔不到1分钟而没有上传成功)。

感想

其实精度问题和环长问题如果有bug基本是自己的代码问题(还是我太菜了hhh),不过两个半小时改需求确实感觉有点短了,毕竟改完还要debug(不得不吐槽一下测评机器,传上去10分钟才出error,本来之前只要两分钟不报错基本都是过了,现在一共2.5小时改代码测评还要这么久就很难顶),不过\r\n这个问题感觉是赛题组的问题了(我们为了保险起见\r和\n都判断了,虽然判了这个但是浮点数判断写错啦!!),毕竟判题系统和赛题都是在Linux下,这换行符搞出一个Win的,确实有点…替大佬们感到惋惜,尤其是top4的大佬们。
不过这一个多月学到的东西不少的,尤其是看大佬们水群可以学到非常多的东西hhh!但是感觉花了太多时间在这个比赛上,有些浪费了(比赛战线太长),也看到了和大佬们巨大的差距,想要找工作还是得努力呀!不过能拿到32强也算比较幸运了吧,毕竟自己是一个菜鸡而且第一次参赛…跟dalao们学到不少东西,要继续努力去刷题啦!!(希望我以后也能随手就是一个baseline,随手就是一个xx算法,哈哈哈)。
然后等比赛结束后也开源一下代码,也算是自己的一点心血吧毕竟花了不少时间,最后祝进决赛的dalao们能有好的表现吧(也希望赛题组…算了不说了)!

代码

看到群里很多大佬都开源了,好像决赛题和这个没什么关系了,那就来开源一下吧!
思路的话基本就是4+3(正向搜4层反向搜3层),太菜了所以几乎没有什么剪枝。看到群里面大佬的交谈,其实很多代码的很多地方都可以做成动态的,根据线上数据来进行数据结构和算法的选择,这些地方应该可以优化不少。

初赛(0.3384)

初赛代码比较烂,多线程是初赛结束前一天半夜改的,因为时间紧来不及当时直接加了两个函数做的多线程,注释也有一些,将就看吧…

#include 
#include 
#include 
#include 
#include 
#include 
#include 


//#define TRACE_INFO
const int MAX_NODE_NUM = 65536;
const int ID_LENGTH = 8;

using namespace std;


/*
**************owen****************
		   2020-4-27
**********************************
1. 终极版优化
***********************************
         score : 0.3384
*/


// 图
int arrGraph[MAX_NODE_NUM][32];
int arrInvGraph[MAX_NODE_NUM][32];


// 符号输出
char IDComma[MAX_NODE_NUM][ID_LENGTH];
char IDLF[MAX_NODE_NUM][ID_LENGTH];


// 两次查表
int MidToNext[MAX_NODE_NUM][32];
int IDToMid[MAX_NODE_NUM][32];
int MidToNextTh1[MAX_NODE_NUM][32];
int IDToMidTh1[MAX_NODE_NUM][32];


// 搜索记忆路径标志位
bool directVisited[MAX_NODE_NUM];
int directPath[8];


// 清零计数
int numClearMidToNext = 0;
int numClearIDToMid = 0;


// 标志位
bool visited[MAX_NODE_NUM];
bool visitedTh1[MAX_NODE_NUM];


int ans3[1000000];
int ans4[1500000];
int ans5[4000000];
int ans6[8000000];
int ans7[21000000];

class DFS {
public:

	// 总体变量
	int recordNum; // 转账记录条数
	int vertexNum;
	int total;
	int szInt; // sizeof(int)
	int* pInputData;
	int* cntGraph;
	int* cntInvGraph;
	int* lengthIDString;

	// 开启线程标志
	bool multiThread = false;

	// 线程1变量
	bool* directVisitedTh1;
	int* directPathTh1;
	int numClearMidToNextTh1 = 0;
	int numClearIDToMidTh1 = 0;
	int startTh1;
	int* pResArrTh1;
	int* cntMidToNextTh1;
	int* cntIDToMidTh1;
	int* clearMidToNextTh1; // 用于清空数据
	int* clearIDToMidTh1;
	int* ansTh1[5];
	int cntAnsTh1[5];

	// 主线程变量
	int start;
	int* pResArr;
	int* cntMidToNext;
	int* cntIDToMid;
	int* clearMidToNext; // 用于清空数据
	int* clearIDToMid;
	int* ans[5];
	int cntAns[5];

	// 构造函数初始化列表初始化值
	DFS() : recordNum(0), vertexNum(0), total(0), start(0), szInt(sizeof(int)) {
		// 共用数据变量
		pInputData = (int*)malloc(560000 * 4);
		cntGraph = (int*)malloc(MAX_NODE_NUM * 4);
		memset(cntGraph, 0, MAX_NODE_NUM * 4);
		cntInvGraph = (int*)malloc(MAX_NODE_NUM * 4);
		memset(cntInvGraph, 0, MAX_NODE_NUM * 4);
		lengthIDString = (int*)malloc(MAX_NODE_NUM * 4);

		// 主线程变量
		cntMidToNext = (int*)malloc(MAX_NODE_NUM * 4);
		memset(cntMidToNext, 0, MAX_NODE_NUM * 4);
		cntIDToMid = (int*)malloc(MAX_NODE_NUM * 4);
		memset(cntIDToMid, 0, MAX_NODE_NUM * 4);
		clearMidToNext = (int*)malloc(8192 * 4);
		clearIDToMid = (int*)malloc(8192 * 4);
		pResArr = (int*)malloc(8 * 4);
		// cntAns赋初值0
		memset(cntAns, 0, szInt * 5);
		ans[0] = ans3;
		ans[1] = ans4;
		ans[2] = ans5;
		ans[3] = ans6;
		ans[4] = ans7;
	}

	~DFS() {
		// 公用变量
		free(cntInvGraph);
		free(cntGraph);
		free(pResArr);
		free(pInputData);
		free(lengthIDString);

		// 主线程变量释放
		free(clearMidToNext);
		free(cntIDToMid);
		free(cntMidToNext);
		if (multiThread)
		{
			// 线程1
			free(directVisitedTh1);
			free(directPathTh1);
			free(clearMidToNextTh1);
			free(cntIDToMidTh1);
			free(cntMidToNextTh1);
			free(clearIDToMidTh1);
			free(pResArrTh1);
			for (int i = 0; i < 5; ++i)
			{
				free(ansTh1[i]);
			}
		}
	}


	// 读取数据
	void ReadData(string& fileName)
	{
		int fd = open(fileName.c_str(), O_RDONLY);
		int fileLength = lseek(fd, 0, SEEK_END);
		char *pMapHead = (char *)mmap(0, fileLength, PROT_READ, MAP_PRIVATE, fd, 0);
		int giver = 0;
        int receiver = 0;
        int number = 0;
        bool state = false;
		for (char *p = pMapHead; p - pMapHead < fileLength; p++)
		{
            if (*p == '\n' || *p == '\r')
			{
				number = 0;
				continue;
			}
			else if (*p == ',')
			{
				if(state==false)
				{
					giver=number;
					state=!state;
				}
                else
				{
					receiver =number;
					state=!state;
                    if(receiver < MAX_NODE_NUM && giver < MAX_NODE_NUM)
                    {
                        pInputData[recordNum++] = giver;
                        pInputData[recordNum++] = receiver;
                    }
				}
				   
				//pInputData[recordNum++] = number;
				number = 0;
				continue;
			}
			number = number * 10 + *p - '0';
		}
		close(fd);
		munmap(pMapHead, fileLength);
#ifdef TRACE_INFO
		cout << "Total records : " << recordNum << endl;
#endif
	}


	// 建图
	void ConstructGraph()
	{
		// 提前定义变量,省去for循环中不停创建销毁的时间
		// 遍历一遍所有输入数据
		for (int i = 0; i < recordNum; i += 2)
		{
			// 出度图和入度图建立
			// 不再需要ID映射
			arrGraph[pInputData[i]][cntGraph[pInputData[i]]++] = pInputData[i + 1];
			arrInvGraph[pInputData[i + 1]][cntInvGraph[pInputData[i + 1]]++] = pInputData[i];
		}
		// 排序去重,后面就相当于索引映射ID号
		sort(pInputData, pInputData + recordNum);
		int* pEnd = unique(pInputData, pInputData + recordNum);
		vertexNum = pEnd - pInputData;
		// 输出时的字符数组
		string tmp;
		int sz = 0;
		for (int i = 0; i < MAX_NODE_NUM; ++i)
		{
			tmp = to_string(i);
			sz = tmp.size();
			memcpy(IDComma[i], tmp.c_str(), sz);
			memcpy(IDLF[i], tmp.c_str(), sz);
			IDComma[i][sz] = ',';
			IDLF[i][sz] = '\n';
			lengthIDString[i] = sz + 1;
		}
#ifdef TRACE_INFO
		cout << "Total IDs : " << vertexNum << endl;
#endif // TRACE_INFO
		// 如果加上拓扑排序,下面这部分就可以不要了,拓扑排序里面也有这部分
		int ID = 0;
		for (int i = 0; i < vertexNum; ++i)
		{
			ID = pInputData[i];
			if (cntGraph[ID] && cntInvGraph[ID])
			{
				sort(arrGraph[ID], arrGraph[ID] + cntGraph[ID]);
				sort(arrInvGraph[ID], arrInvGraph[ID] + cntInvGraph[ID]);
			}
			else
			{
				visited[ID] = true;
				visitedTh1[ID] = true;
			}
		}
	}


	void MultiThreadInit()
	{
		multiThread = true;
		// 线程1
		directVisitedTh1 = (bool*)malloc(MAX_NODE_NUM);
		memset(directVisitedTh1, false, MAX_NODE_NUM);
		directPathTh1 = (int*)malloc(8 * szInt);
		cntMidToNextTh1 = (int*)malloc(MAX_NODE_NUM * 4);
		memset(cntMidToNextTh1, 0, MAX_NODE_NUM * 4);
		cntIDToMidTh1 = (int*)malloc(MAX_NODE_NUM * 4);
		memset(cntIDToMidTh1, 0, MAX_NODE_NUM * 4);
		clearMidToNextTh1 = (int*)malloc(8192 * 4);
		clearIDToMidTh1 = (int*)malloc(8192 * 4);
		pResArrTh1 = (int*)malloc(8 * 4);
		// cntAns赋初值0
		memset(cntAnsTh1, 0, szInt * 5);
		ansTh1[0] = (int*)malloc(2 * 500000 * 4);
		ansTh1[1] = (int*)malloc(3 * 500000 * 4);
		ansTh1[2] = (int*)malloc(4 * 1000000 * 4);
		ansTh1[3] = (int*)malloc(8 * 1000000 * 4);
		ansTh1[4] = (int*)malloc(7 * 3000000 * 4);
	}

	void DirectPathDFS(int current, int depth)
	{
		// 递归条件
		// table1
		if (depth == 3 && !directVisited[current])
		{
			MidToNext[current][cntMidToNext[current]++] = directPath[1];
			sort(MidToNext[current], MidToNext[current] + cntMidToNext[current]);
			cntMidToNext[current] = unique(MidToNext[current], MidToNext[current] + cntMidToNext[current]) - MidToNext[current];
			clearMidToNext[numClearMidToNext++] = current;
		}
		// table2
		if (depth == 4 && !directVisited[current])
		{
			IDToMid[current][cntIDToMid[current]++] = directPath[2];
			sort(IDToMid[current], IDToMid[current] + cntIDToMid[current]);
			cntIDToMid[current] = unique(IDToMid[current], IDToMid[current] + cntIDToMid[current]) - IDToMid[current];
			clearIDToMid[numClearIDToMid++] = current;
		}
		// TODO
		directPath[depth - 1] = current;
		directVisited[current] = true;
		if (depth < 4)
		{
			int iter = 0;
			while (iter < cntInvGraph[current] && arrInvGraph[current][iter] < start)
				++iter;
			if (iter == cntInvGraph[current] - 1)
			{
				directVisited[current] = true;
			}
			// 递归寻找下一个点
			for (; iter < cntInvGraph[current]; ++iter)
			{
				if (!directVisited[arrInvGraph[current][iter]])
				{
					DirectPathDFS(arrInvGraph[current][iter], depth + 1);
				}
			}
		}
		// 回溯
		directVisited[current] = false;
	}

	void dfs(int current, int depth)
	{
		// 路径长度大于等于2且可以直接根据直接路径到达,减少一次深度递归
		pResArr[depth - 1] = current;
		visited[current] = true;
		if (cntIDToMid[current])
		{
			for (int i = 0; i < cntIDToMid[current]; ++i)
			{
				if (!visited[IDToMid[current][i]])
				{
					//int lasttwo_ID = IDToMid[current][i];
					pResArr[depth] = IDToMid[current][i];
					for (int j = 0; j < cntMidToNext[IDToMid[current][i]]; ++j)
					{
						int last_ID = MidToNext[IDToMid[current][i]][j];
						if (!visited[last_ID])
						{
							pResArr[depth + 1] = last_ID;
							switch (depth + 2)
							{
							case 7:
								memcpy(ans[4] + 7 * cntAns[4]++, pResArr, 7 * szInt);
								continue;
							case 6:
								memcpy(ans[3] + 6 * cntAns[3]++, pResArr, 6 * szInt);
								continue;
							case 5:
								memcpy(ans[2] + 5 * cntAns[2]++, pResArr, 5 * szInt);
								continue;
							case 4:
								memcpy(ans[1] + 4 * cntAns[1]++, pResArr, 4 * szInt);
								continue;
							}
						}
					}
				}
			}
		}
		if (depth < 5)
		{
			for (int i = 0; i < cntGraph[current]; ++i)
			{
				if (!visited[arrGraph[current][i]] && arrGraph[current][i] > start)
				{
					dfs(arrGraph[current][i], depth + 1);
				}

				else if (depth == 3 && arrGraph[current][i] == start && visited[arrGraph[current][i]])
				{
					memcpy(ans[0] + 3 * cntAns[0]++, pResArr, 3 * szInt);
				}
			}
		}
		// 回溯
		visited[current] = false;
	}

	void FindCircle(int end)
	{
		int ID = 0;
		// 对每个点进行dfs
		for (int i = 0; i < end; ++i)
		{
#ifdef TRACE_INFO
			/*if (i % 10000 == 0)
				cout << i << "/" << MAX_NODE_NUM << endl;*/
#endif // TRACE_INFO	
			ID = pInputData[i];
			if (!visited[ID])
			{
				start = ID;
				DirectPathDFS(start, 1);
				dfs(start, 1);
				// 清空表2标志位 
				for (int k = 0; k < numClearIDToMid; ++k)
				{
					cntIDToMid[clearIDToMid[k]] = 0;
				}
				// 清空表1标志位
				for (int k = 0; k < numClearMidToNext; ++k)
				{
					cntMidToNext[clearMidToNext[k]] = 0;
				}
				numClearMidToNext = 0;
				numClearIDToMid = 0;
			}
		}
	}

	void th1_DirectPathDFS(int current, int depth)
	{
		// 递归条件
		// table1
		if (depth == 3 && !directVisitedTh1[current])
		{
			MidToNextTh1[current][cntMidToNextTh1[current]++] = directPathTh1[1];
			sort(MidToNextTh1[current], MidToNextTh1[current] + cntMidToNextTh1[current]);
			cntMidToNextTh1[current] = unique(MidToNextTh1[current], MidToNextTh1[current] + cntMidToNextTh1[current]) - MidToNextTh1[current];
			clearMidToNextTh1[numClearMidToNextTh1++] = current;
		}
		// table2
		if (depth == 4 && !directVisitedTh1[current])
		{
			IDToMidTh1[current][cntIDToMidTh1[current]++] = directPathTh1[2];
			sort(IDToMidTh1[current], IDToMidTh1[current] + cntIDToMidTh1[current]);
			cntIDToMidTh1[current] = unique(IDToMidTh1[current], IDToMidTh1[current] + cntIDToMidTh1[current]) - IDToMidTh1[current];
			clearIDToMidTh1[numClearIDToMidTh1++] = current;
		}
		// TODO
		directPathTh1[depth - 1] = current;
		directVisitedTh1[current] = true;
		if (depth < 4)
		{
			int iter = 0;
			while (iter < cntInvGraph[current] && arrInvGraph[current][iter] < startTh1)
				++iter;
			if (iter == cntInvGraph[current] - 1)
			{
				directVisitedTh1[current] = true;
			}
			// 递归寻找下一个点
			for (; iter < cntInvGraph[current]; ++iter)
			{
				if (!directVisitedTh1[arrInvGraph[current][iter]])
				{
					th1_DirectPathDFS(arrInvGraph[current][iter], depth + 1);
				}
			}
		}
		// 回溯
		directVisitedTh1[current] = false;
	}

	void th1_dfs(int current, int depth)
	{
		// 路径长度大于等于2且可以直接根据直接路径到达,减少一次深度递归
		pResArrTh1[depth - 1] = current;
		visitedTh1[current] = true;
		if (cntIDToMidTh1[current])
		{
			for (int i = 0; i < cntIDToMidTh1[current]; ++i)
			{
				if (!visitedTh1[IDToMidTh1[current][i]])
				{
					//int lasttwo_ID = IDToMidTh1[current][i];
					pResArrTh1[depth] = IDToMidTh1[current][i];
					for (int j = 0; j < cntMidToNextTh1[IDToMidTh1[current][i]]; ++j)
					{
						int last_ID = MidToNextTh1[IDToMidTh1[current][i]][j];
						if (!visitedTh1[last_ID])
						{
							pResArrTh1[depth + 1] = last_ID;
							switch (depth + 2)
							{
							case 7:
								memcpy(ansTh1[4] + 7 * cntAnsTh1[4]++, pResArrTh1, 7 * szInt);
								continue;
							case 6:
								memcpy(ansTh1[3] + 6 * cntAnsTh1[3]++, pResArrTh1, 6 * szInt);
								continue;
							case 5:
								memcpy(ansTh1[2] + 5 * cntAnsTh1[2]++, pResArrTh1, 5 * szInt);
								continue;
							case 4:
								memcpy(ansTh1[1] + 4 * cntAnsTh1[1]++, pResArrTh1, 4 * szInt);
								continue;
							}
						}
					}
				}
			}
		}
		if (depth < 5)
		{
			for (int i = 0; i < cntGraph[current]; ++i)
			{
				if (!visitedTh1[arrGraph[current][i]] && arrGraph[current][i] > startTh1)
				{
					th1_dfs(arrGraph[current][i], depth + 1);
				}

				else if (depth == 3 && arrGraph[current][i] == startTh1 && visitedTh1[arrGraph[current][i]])
				{
					memcpy(ansTh1[0] + 3 * cntAnsTh1[0]++, pResArrTh1, 3 * szInt);
				}
			}
		}
		// 回溯
		visitedTh1[current] = false;
	}

	void th1_FindCircle(int begin, int end)
	{
		int IDTh1 = 0;
		// 对每个点进行dfs
		for (int i = begin; i < end; ++i)
		{
#ifdef TRACE_INFO
			/*if (i % 10000 == 0)
				cout << i << "/" << MAX_NODE_NUM << endl;*/
#endif // TRACE_INFO
			IDTh1 = pInputData[i];
			if (!visitedTh1[IDTh1])
			{
				startTh1 = IDTh1;
				th1_DirectPathDFS(startTh1, 1);
				th1_dfs(startTh1, 1);
				// 清空表2标志位 
				for (int k = 0; k < numClearIDToMidTh1; ++k)
				{
					cntIDToMidTh1[clearIDToMidTh1[k]] = 0;
				}
				// 清空表1标志位
				for (int k = 0; k < numClearMidToNextTh1; ++k)
				{
					cntMidToNextTh1[clearMidToNextTh1[k]] = 0;
				}
				numClearMidToNextTh1 = 0;
				numClearIDToMidTh1 = 0;
			}
		}
	}


#define MAX_BUFFER_SIZE 4194304
	// 输出结果
	void OutputResult(string& fileName)
	{
		FILE* file = fopen(fileName.c_str(), "w");
		// 计算总环数
		total += cntAns[0] + cntAns[1] + cntAns[2] + cntAns[3] + cntAns[4];
		char* res = new char[MAX_BUFFER_SIZE];
		res[MAX_BUFFER_SIZE - 1] = '\0';
		string numPaths = to_string(total) + "\n";
		int sz = numPaths.size(), IDSize = 0, limit = MAX_BUFFER_SIZE - 128;
		memcpy(res, numPaths.c_str(), sz);

		// 顺序输出
		for (int len = 0; len < 5; ++len)
		{
			for (int i = 0; i < cntAns[len]; ++i)
			{
				for (int j = 0; j < len + 2; ++j)
				{
					IDSize = lengthIDString[ans[len][i * (len + 3) + j]];
					memcpy(res + sz, IDComma[ans[len][i * (len + 3) + j]], IDSize);
					sz += IDSize;
				}

				IDSize = lengthIDString[ans[len][i * (len + 3) + len + 2]];
				memcpy(res + sz, IDLF[ans[len][i * (len + 3) + len + 2]], IDSize);
				sz += IDSize;

				if (sz > limit)
				{
					res[sz] = '\0';
					fwrite(res, 1, sz, file);
					sz = 0;
				}
			}
		}
		res[sz] = '\0';
		fwrite(res, 1, sz, file);
		delete[] res;
		fclose(file);
	}

	void MultiOutput(string& fileName)
	{
		FILE* file = fopen(fileName.c_str(), "w");
		// 计算总环数
		total += cntAns[0] + cntAns[1] + cntAns[2] + cntAns[3] + cntAns[4]
			+ cntAnsTh1[0] + cntAnsTh1[1] + cntAnsTh1[2] + cntAnsTh1[3] + cntAnsTh1[4];
		char* res = new char[MAX_BUFFER_SIZE];
		res[MAX_BUFFER_SIZE - 1] = '\0';
		string numPaths = to_string(total) + "\n";
		int sz = numPaths.size(), IDSize = 0, limit = MAX_BUFFER_SIZE - 128;
		memcpy(res, numPaths.c_str(), sz);

		// 顺序输出
		for (int len = 0; len < 5; ++len)
		{
			for (int i = 0; i < cntAns[len]; ++i)
			{
				for (int j = 0; j < len + 2; ++j)
				{
					IDSize = lengthIDString[ans[len][i * (len + 3) + j]];
					memcpy(res + sz, IDComma[ans[len][i * (len + 3) + j]], IDSize);
					sz += IDSize;
				}

				IDSize = lengthIDString[ans[len][i * (len + 3) + len + 2]];
				memcpy(res + sz, IDLF[ans[len][i * (len + 3) + len + 2]], IDSize);
				sz += IDSize;

				if (sz > limit)
				{
					res[sz] = '\0';
					fwrite(res, 1, sz, file);
					sz = 0;
				}
			}
			// 线程1结果
			for (int i = 0; i < cntAnsTh1[len]; ++i)
			{
				for (int j = 0; j < len + 2; ++j)
				{
					IDSize = lengthIDString[ansTh1[len][i * (len + 3) + j]];
					memcpy(res + sz, IDComma[ansTh1[len][i * (len + 3) + j]], IDSize);
					sz += IDSize;
				}

				IDSize = lengthIDString[ansTh1[len][i * (len + 3) + len + 2]];
				memcpy(res + sz, IDLF[ansTh1[len][i * (len + 3) + len + 2]], IDSize);
				sz += IDSize;

				if (sz > limit)
				{
					res[sz] = '\0';
					fwrite(res, 1, sz, file);
					sz = 0;
				}
			}
		}
		res[sz] = '\0';
		fwrite(res, 1, sz, file);
		delete[] res;
		fclose(file);
	}

};


int main()
{
#ifdef TRACE_INFO
	clock_t start_time, dfs_time, end_time, read_time, dre_end, cons_time;
#endif // TRACE_INFO

	string fileName("/data/test_data.txt");
	string outputName("/projects/student/result.txt");

#ifdef TRACE_INFO
	start_time = clock();
#endif // TRACE_INFO

	DFS dfs;
	dfs.ReadData(fileName);
#ifdef TRACE_INFO
	read_time = clock();
	cout << "read data : " << (double)(read_time - start_time) / CLOCKS_PER_SEC << "s" << endl;
#endif // TRACE_INFO

	dfs.ConstructGraph();

#ifdef TRACE_INFO
	cons_time = clock();
	cout << "construct graph : " << (double)(cons_time - read_time) / CLOCKS_PER_SEC << "s" << endl;
#endif // TRACE_INFO


#ifdef TRACE_INFO
	dre_end = clock();
#endif // TRACE_INFO


	if (dfs.vertexNum > 8888)
	{
		dfs.MultiThreadInit();
		int multiThreadNum1 = dfs.vertexNum / 10;
		thread th1(&DFS::th1_FindCircle, &dfs, multiThreadNum1, dfs.vertexNum);
		dfs.FindCircle(multiThreadNum1);
		th1.join();

#ifdef TRACE_INFO
		dfs_time = clock();
		cout << "dfs time : " << (double)(dfs_time - dre_end) / CLOCKS_PER_SEC << "s" << endl;
#endif // TRACE_INFO
		dfs.MultiOutput(outputName);
#ifdef TRACE_INFO
		end_time = clock();
		cout << "output time : " << (double)(end_time - dfs_time) / CLOCKS_PER_SEC << "s" << endl;
		cout << "total time : " << (double)(end_time - start_time) / CLOCKS_PER_SEC << "s" << endl;
		cout << "Total Loops : " << dfs.total << endl;
#endif // TRACE_INFO
	}
	else
	{
		dfs.FindCircle(dfs.vertexNum);
#ifdef TRACE_INFO
		dfs_time = clock();
		cout << "dfs time : " << (double)(dfs_time - dre_end) / CLOCKS_PER_SEC << "s" << endl;
#endif // TRACE_INFO
		dfs.OutputResult(outputName);
#ifdef TRACE_INFO
		end_time = clock();
		cout << "output time : " << (double)(end_time - dfs_time) / CLOCKS_PER_SEC << "s" << endl;
		cout << "total time : " << (double)(end_time - start_time) / CLOCKS_PER_SEC << "s" << endl;
		cout << "Total Loops : " << dfs.total << endl;
#endif // TRACE_INFO
	}

	return 0;
}

复赛

复赛用了4线程8线程,也对代码封装了一下,稍微好懂一点,基本思路也没变,有分之后没怎么优化了(主要是看着前排大佬太猛了,就算是放弃)
因为提交版本的代码用vscode打开注释乱码了,所以放的是之前在IDE写的,main函数没改,可能跑不通。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define TRACE_INFO

using namespace std;

/***********owen***********//*
----------20200510-----------
           八线程
-----------------------------
*//*************************/


const int MAX_RECODR = 2000000;
// 最长ID字符16
const int ID_LENGTH = 16;
// 初始化分配的结果数组长度500W,如果不够*2递增
const int INIT_RESULT_LENGTH = 2000000;
// 最大缓冲区4M
const int MAX_BUFFER_SIZE = 4194304;
// 最大线程数
const int MAX_THREAD = 8;


typedef unsigned int ui;
typedef unsigned long long ull;


typedef struct _GraphData
{
	ui ID;
	ull money;
	_GraphData() : ID(0), money(0) {}
	_GraphData(ui first, ull fm) : ID(first), money(fm) {}
}GraphData;


class MyGraph {
public:

	GraphData* pData = NULL; //存储数据的数组
	unsigned size; //数组元素个数
	unsigned capicity; //最大可存储空间
	int iter;

	//默认构造函数
	MyGraph() : size(0), capicity(32), iter(0) {
		pData = (GraphData*)malloc(capicity * sizeof(GraphData));
	};

	//预留空间构造函数
	MyGraph(int MaxNum) : size(0), capicity(MaxNum), iter(0) {
		pData = (GraphData*)malloc(capicity * sizeof(GraphData));
	};

	~MyGraph() {
		if (pData != NULL)
			free(pData);
	};

	//插入一个元素到最后
	void emplace_back(const GraphData& element) {
		if (size == capicity)
		{
			capicity *= 2;
			GraphData* dataTemp = (GraphData*)malloc(capicity * sizeof(GraphData));
			memcpy(dataTemp, pData, size * sizeof(GraphData));
			free(pData);
			pData = dataTemp;
		}
		// 插入自动有序
		iter = size - 1;
		while (iter > -1 && pData[iter].ID > element.ID)
		{
			pData[iter + 1].ID = pData[iter].ID;
			pData[iter + 1].money = pData[iter].money;
			--iter;
		}
		pData[iter + 1].ID = element.ID;
		pData[iter + 1].money = element.money;
		// 尾指针递增
		++size;
	}
};


// 自定义int数组类模拟vector
class MyVector {

public:

	int* pArray = NULL; //存储数据的数组
	unsigned size; //数组元素个数
	unsigned capicity; //最大可存储空间
	int iter;

	//默认构造函数
	MyVector() : size(0), capicity(16), iter(0) {
		pArray = (int*)malloc(capicity * sizeof(int));
	};

	//预留空间构造函数
	MyVector(int MaxNum) : size(0), capicity(MaxNum), iter(0) {
		pArray = (int*)malloc(capicity * sizeof(int));
	};

	~MyVector() {
		if (pArray != NULL)
			free(pArray);
	};

	// 预先分配内存
	void reserve(const int MaxNum)
	{
		if (MaxNum > capicity)
		{
			capicity = MaxNum;
			int* dataTemp = (int*)malloc(capicity * sizeof(int));
			memcpy(dataTemp, pArray, size * sizeof(int));
			free(pArray);
			pArray = dataTemp;
		}
	}

	// 为路径预先分配内存
	void reserve_path(const int MaxNum, int pathLen)
	{
		if (MaxNum > capicity)
		{
			capicity = MaxNum;
			int* dataTemp = (int*)malloc(capicity * sizeof(int) * pathLen);
			memcpy(dataTemp, pArray, size * sizeof(int) * pathLen);
			free(pArray);
			pArray = dataTemp;
		}
	}

	// 数组使用添加元素
	// 动态扩容,并插入一个元素到最后,每次容量扩大四倍
	void push_back(const int& element)
	{
		if (size == capicity)
		{
			capicity *= 4;
			int* dataTemp = (int*)malloc(capicity * sizeof(int));
			memcpy(dataTemp, pArray, size * sizeof(int));
			free(pArray);
			pArray = dataTemp;
		}
		// 插入元素到尾部并自增size
		pArray[size++] = element;
	};

	// 添加结果路径,扩容每次*2
	// 这里size代表路径条数,每个类实例化之后pathLen是不同的
	void emplace_path(int* pathHead, const int& pathLen)
	{
		if (size * pathLen > capicity)
		{
			capicity *= 2;
			int* dataTemp = (int*)malloc(capicity * sizeof(int) * pathLen);
			memcpy(dataTemp, pArray, size * sizeof(int) * pathLen);
			free(pArray);
			pArray = dataTemp;
		}
		// 结果路径拷贝到当前数组中,且自增
		memcpy(pArray + pathLen * size++, pathHead, pathLen * 4);
	};
};



typedef struct _StructPair
{
	int first;
	ull firstMoney;
	int second;
	ull secondMoney;
	_StructPair() : first(0), firstMoney(0), second(0), secondMoney(0) {}
	_StructPair(int first, ull fm, int second, ull sm) : first(first), firstMoney(fm), second(second), secondMoney(sm) {}
}StructPair;


class MyPair
{
public:
	// 动态分配内存
	StructPair* pStructPair = NULL;
	int size;
	int capicity;

	MyPair() : size(0), capicity(4)
	{
		// 初始化分配空间为2个结构体,即2*8
		pStructPair = (StructPair*)malloc(capicity * 32);
	}

	~MyPair()
	{
		if (pStructPair != NULL)
			free(pStructPair);
	}


	// 自定义尾部添加元素函数
	void push_back(int first, ull fm, int second, ull sm)
	{
		if (size == capicity)
		{
			capicity *= 4;
			StructPair* newPair = (StructPair*)malloc(capicity * 32);
			memcpy(newPair, pStructPair, size * 32);
			free(pStructPair);
			pStructPair = newPair;
		}
		int iter = size - 1;
		while (iter > -1 && pStructPair[iter].first > first)
		{
			pStructPair[iter + 1].first = pStructPair[iter].first;
			pStructPair[iter + 1].firstMoney = pStructPair[iter].firstMoney;
			pStructPair[iter + 1].second = pStructPair[iter].second;
			pStructPair[iter + 1].secondMoney = pStructPair[iter].secondMoney;
			--iter;
		}
		pStructPair[iter + 1].first = first;
		pStructPair[iter + 1].firstMoney = fm;
		pStructPair[iter + 1].second = second;
		pStructPair[iter + 1].secondMoney = sm;
		// 尾指针递增
		++size;
	}

};


/*******************************************//*

		以上是自定义数据结构及变量定义
		以下是所有需要的数据结构初始化

*//*******************************************/


// 图
MyGraph* arrGraph;
MyGraph* arrInvGraph;


// ID映射
unordered_map<ui, int> IDMap;


// 结果输出
char** IDComma;
char** IDLF;
int* lengthIDString;


// 全局变量存放结果
MyVector th1_ans[5];
MyVector th2_ans[5];
MyVector th3_ans[5];
MyVector th4_ans[5];
MyVector th5_ans[5];
MyVector th6_ans[5];
MyVector th7_ans[5];
MyVector th8_ans[5];
// 指针数组
MyVector* g_ans[8] = { th1_ans, th2_ans, th3_ans, th4_ans, th5_ans, th6_ans, th7_ans, th8_ans };


// 数据读取有关参数
int recordNum = 0;
int IDRecord = 0;
int vertexNum = 0;// 总结点数
int total = 0;
ull* pInputData;
ui* pInputID;
ui* newInputID;

// 是否访问标志位,直接预分配四个线程
bool* g_visited;


// 读取数据
void ReadData(string& fileName)
{
	// 初始化读数据所需的数组
	pInputData = (ull*)malloc(MAX_RECODR * 8);
	pInputID = (ui*)malloc(MAX_RECODR * 4 * 2);
	FILE* file = fopen(fileName.c_str(), "r");
	ui giverID, receiverID;
	ull money;
	while (fscanf(file, "%u,%u,%llu", &giverID, &receiverID, &money) != EOF)
	{
		// 数据添加到同一个数组
		pInputID[IDRecord++] = giverID;
		pInputID[IDRecord++] = receiverID;
		pInputData[recordNum++] = money;
	}

#ifdef TRACE_INFO
	cout << "Total records : " << recordNum << endl;
#endif	
}


// 建图
void ConstructGraph()
{
	newInputID = (ui*)malloc(IDRecord * 4);
	memcpy(newInputID, pInputID, IDRecord * 4);
	// 排序去重,后面就相当于索引映射ID号
	sort(pInputID, pInputID + IDRecord);
	ui* pEnd = unique(pInputID, pInputID + IDRecord);
	vertexNum = pEnd - pInputID;
	// 初始化分配答案数组
	for (int i = 0; i < MAX_THREAD; ++i)
	{
		for (int j = 0; j < 5; ++j)
		{
			// reserve函数是malloc的int类型数量,所以要乘以结果长度
			g_ans[i][j].reserve_path(INIT_RESULT_LENGTH, (j + 3));
		}
	}
	// ID映射
	IDMap.reserve(vertexNum); // 预留空间
	for (int i = 0; i < vertexNum; ++i)
	{
		IDMap[pInputID[i]] = i;
	}
	// 建立图的第一维数组
	arrGraph = new MyGraph[vertexNum];
	arrInvGraph = new MyGraph[vertexNum];
	// 提前定义变量,省去for循环中不停创建销毁的时间
	// 遍历一遍所有输入数据
	for (int i = 0; i < recordNum; ++i)
	{
		// 第一维是映射,第二维是ID号
		arrGraph[IDMap[newInputID[i * 2]]].emplace_back(GraphData(IDMap[newInputID[i * 2 + 1]], pInputData[i]));
		arrInvGraph[IDMap[newInputID[i * 2 + 1]]].emplace_back(GraphData(IDMap[newInputID[i * 2]], pInputData[i]));
	}
	// 输出时的字符数组
	lengthIDString = (int*)malloc(vertexNum * 4);
	IDComma = (char**)malloc(vertexNum * 4 * ID_LENGTH);
	IDLF = (char**)malloc(vertexNum * 4 * ID_LENGTH);
	for (int i = 0; i < vertexNum; ++i)
	{
		IDComma[i] = (char*)malloc(ID_LENGTH);
		IDLF[i] = (char*)malloc(ID_LENGTH);
	}
	string tmp;
	int sz = 0;
	for (int i = 0; i < vertexNum; ++i)
	{
		tmp = to_string(pInputID[i]);
		sz = tmp.size();
		memcpy(IDComma[i], tmp.c_str(), sz);
		memcpy(IDLF[i], tmp.c_str(), sz);
		IDComma[i][sz] = ',';
		IDLF[i][sz] = '\n';
		lengthIDString[i] = sz + 1;
	}
#ifdef TRACE_INFO
	cout << "Total IDs : " << vertexNum << endl;
#endif // TRACE_INFO
	// 首先初始化visited数组
	g_visited = (bool*)malloc(vertexNum);
	memset(g_visited, false, vertexNum);
	for (int i = 0; i < vertexNum; ++i)
	{
		if (!arrGraph[i].size || !arrInvGraph[i].size)
		{
			g_visited[i] = true;
		}
	}
}


// 释放数据
void FreeData()
{
	//free(IDLF);
	//free(IDComma);
	free(newInputID);
	free(pInputID);
	free(pInputData);
	free(lengthIDString);
	delete[] arrInvGraph;
	delete[] arrGraph;
}



class DFS {
public:
	// 线程标志号
	int threadNum;

	// MyPair数组指针
	MyPair* pMyPair;
	// 清空MyPair的数组类
	MyVector clearMyPair;
	// 总体变量
	int start; // 寻找环的起点
	int szInt; // sizeof(int)
	ull checkMoney[8];
	ull checkInvMoney[8];
	ull* lastToStart;

	// 当前路径结果数组
	int* pResArr;
	// 搜索记忆路径标志位
	bool* directVisited;
	int directPath[8];
	// 标志位
	bool* visited;


	// 构造函数初始化列表初始化值
	DFS(int thNum) : start(0), szInt(sizeof(int)) {
		// 线程号
		threadNum = thNum;
		// 暂时存放结果路径数组
		pResArr = (int*)malloc(8 * 4);
		// 需要根据节点数量变化的变量初始化
		visited = (bool*)malloc(vertexNum);
		//memset(visited, false, vertexNum);
		directVisited = (bool*)malloc(vertexNum);
		memset(directVisited, false, vertexNum);
		lastToStart = (ull*)malloc(vertexNum * 8);

		// new出来MyPair数组
		pMyPair = new MyPair[vertexNum];
		// 预分配一部分空间
		clearMyPair.reserve(vertexNum);
		// 结果数组预分配空间
	}

	~DFS() {
		free(lastToStart);
		free(directVisited);
		free(visited);
		free(pResArr);
		delete[] pMyPair;
	}


	void DirectPathDFS(int current, int depth)
	{
		int  next = 0, mid = 0, ID = 0;
		for (int v = 0; v < arrInvGraph[current].size; ++v)
		{
			// arrInvGraph[i][v]-节点i的第v个入度边的节点序号
			// 如果该点没有入度边,continue
			next = arrInvGraph[current].pData[v].ID;
			if (current > next || arrInvGraph[next].size == 0) continue;
			checkInvMoney[0] = arrInvGraph[current].pData[v].money;
			lastToStart[next] = checkInvMoney[0];
			for (int c = 0; c < arrInvGraph[next].size; ++c)
			{
				if (3 * arrInvGraph[next].pData[c].money >= checkInvMoney[0] && arrInvGraph[next].pData[c].money <= 5 * checkInvMoney[0])
				{
					mid = arrInvGraph[next].pData[c].ID;
					// arrInvGraph[next][c]-节点v的第c个入度边的序号
					if (current > mid || arrInvGraph[mid].size == 0) continue;
					checkInvMoney[1] = arrInvGraph[next].pData[c].money;
					for (int it = 0; it < arrInvGraph[mid].size; ++it)
					{
						if (3 * arrInvGraph[mid].pData[it].money >= checkInvMoney[1] && arrInvGraph[mid].pData[it].money <= 5 * checkInvMoney[1])
						{
							ID = arrInvGraph[mid].pData[it].ID;
							// arrInvGraph[mid][it]-节点c的第it个入度边的序号
							if (ID == next || mid == next) continue;
							else if (ID > current)
							{
								checkInvMoney[2] = arrInvGraph[mid].pData[it].money;
								pMyPair[ID].push_back(mid, checkInvMoney[2], next, checkInvMoney[1]);
								clearMyPair.push_back(ID);
							}
						}
					}
				}
			}
		}
	}

	int last_ID = 0;

	void dfs(int current, int depth)
	{
		pResArr[depth - 1] = current;
		visited[current] = true;
		// 路径长度大于等于2且可以直接根据直接路径到达,减少一次深度递归
		for (int i = 0, sz = pMyPair[current].size; i < sz; ++i)
		{
			// 这里结构体没加括号不知道会不会影响
			StructPair& pair = pMyPair[current].pStructPair[i];
			if (!visited[pair.first] && !visited[pair.second])
			{
				if (5 * pair.firstMoney >= checkMoney[depth - 1 - 1] && pair.firstMoney <= 3 * checkMoney[depth - 1 - 1])
				{
					pResArr[depth] = pair.first;
					checkMoney[depth - 1] = pair.firstMoney;
					if (5 * pair.secondMoney >= checkMoney[depth - 1] && pair.secondMoney <= 3 * checkMoney[depth - 1])
					{
						last_ID = pair.second;
						checkMoney[depth] = pair.secondMoney;
						if (5 * checkMoney[0] >= lastToStart[last_ID] && checkMoney[0] <= 3 * lastToStart[last_ID])
						{
							pResArr[depth + 1] = last_ID;
							g_ans[threadNum][depth - 1].emplace_path(pResArr, depth + 2);
						}
					}
				}
			}
		}
		if (depth < 5)
		{
			for (int i = 0; i < arrGraph[current].size; ++i)
			{
				if (arrGraph[current].pData[i].ID > start && !visited[arrGraph[current].pData[i].ID])
				{
					if (depth == 1)
					{
						checkMoney[depth - 1] = arrGraph[current].pData[i].money;
						dfs(arrGraph[current].pData[i].ID, depth + 1);
					}
					else if (5 * arrGraph[current].pData[i].money >= (checkMoney[depth - 1 - 1]) && arrGraph[current].pData[i].money <= 3 * (checkMoney[depth - 1 - 1]))
					{
						checkMoney[depth - 1] = arrGraph[current].pData[i].money;
						dfs(arrGraph[current].pData[i].ID, depth + 1);
					}
				}
				else if (depth == 3 && arrGraph[current].pData[i].ID == start && visited[start])
				{
					if (5 * arrGraph[current].pData[i].money >= checkMoney[depth - 1 - 1] && arrGraph[current].pData[i].money <= 3 * checkMoney[depth - 1 - 1])
					{
						if (5 * checkMoney[0] >= arrGraph[current].pData[i].money && checkMoney[0] <= 3 * arrGraph[current].pData[i].money)
						{
							g_ans[threadNum][0].emplace_path(pResArr, 3);
						}
					}
				}
			}
		}
		// 回溯
		visited[current] = false;
	}


	// 找环
	void FindCircle(int begin, int end)
	{
		for (int i = 0; i < vertexNum; ++i)
		{
			visited[i] = g_visited[i];
		}
		// 对每个点进行dfs
		for (int i = begin; i < end; ++i)
		{
#ifdef TRACE_INFO
			if (i % 10000 == 0)
				cout << i << "/" << vertexNum << endl;
#endif // TRACE_INFO
			if (!visited[i])
			{
				start = i;
				DirectPathDFS(start, 1);
				dfs(start, 1);
				// dfs完之后清空标志位以及计数器
				for (int k = 0; k < clearMyPair.size; ++k)
				{
					pMyPair[clearMyPair.pArray[k]].size = 0;
				}
				clearMyPair.size = 0;
			}
		}
	}
};


void MultiOutput(string& fileName)
{
	FILE* file = fopen(fileName.c_str(), "w");
	// 计算总环数
	for (int i = 0; i < MAX_THREAD; ++i)
	{
		for (int j = 0; j < 5; ++j)
		{
			total += g_ans[i][j].size;
		}
	}
	char* res = new char[MAX_BUFFER_SIZE];
	res[MAX_BUFFER_SIZE - 1] = '\0';
	string numPaths = to_string(total) + "\n";
	int sz = numPaths.size(), IDSize = 0, limit = MAX_BUFFER_SIZE - 128;
	memcpy(res, numPaths.c_str(), sz);

	// 顺序输出
	for (int len = 0; len < 5; ++len)
	{
		for (int th = 0; th < MAX_THREAD; ++th)
		{
			for (int i = 0; i < g_ans[th][len].size; ++i)
			{
				for (int j = 0; j < len + 2; ++j)
				{
					IDSize = lengthIDString[g_ans[th][len].pArray[i * (len + 3) + j]];
					memcpy(res + sz, IDComma[g_ans[th][len].pArray[i * (len + 3) + j]], IDSize);
					sz += IDSize;
				}

				IDSize = lengthIDString[g_ans[th][len].pArray[i * (len + 3) + len + 2]];
				memcpy(res + sz, IDLF[g_ans[th][len].pArray[i * (len + 3) + len + 2]], IDSize);
				sz += IDSize;

				if (sz > limit)
				{
					res[sz] = '\0';
					fwrite(res, 1, sz, file);
					sz = 0;
				}
			}
		}
	}
	res[sz] = '\0';
	fwrite(res, 1, sz, file);
	delete[] res;
	fclose(file);
}


int main()
{
#ifdef TRACE_INFO
	clock_t start_time, dfs_time, end_time, read_time, out_time, cons_time;
#endif // TRACE_INFO
	
	//string fileName("/data/test_data.txt");
	//string outputName("/projects/student/result.txt");
	//string fileName("test_data_38252_9153.txt");
	string fileName("test_data.txt");
	//string fileName("test_data_300W.txt");
	//string fileName("test_data_1960W.txt");
	//string fileName("test_data_280W.txt");
	string outputName("test_result.txt");

#ifdef TRACE_INFO
	start_time = clock();
#endif // TRACE_INFO

	ReadData(fileName);

#ifdef TRACE_INFO
	read_time = clock();
	cout << "read data : " << (double)(read_time - start_time) / CLOCKS_PER_SEC << "s" << endl;
#endif // TRACE_INFO

	ConstructGraph();

#ifdef TRACE_INFO
	cons_time = clock();
	cout << "construct graph : " << (double)(cons_time - read_time) / CLOCKS_PER_SEC << "s" << endl;
#endif // TRACE_INFO

	// 多线程开启条件
	if (vertexNum > 150000)
	{
		DFS dfs1(0);
		DFS dfs2(1);
		DFS dfs3(2);
		DFS dfs4(3);
		DFS dfs5(4);
		DFS dfs6(5);
		DFS dfs7(6);
		DFS dfs8(7);

		thread t1(&DFS::FindCircle, &dfs1, 0, 0.0154 * vertexNum);
		thread t2(&DFS::FindCircle, &dfs2, 0.0154 * vertexNum, 0.07 * vertexNum);
		thread t3(&DFS::FindCircle, &dfs3, 0.07 * vertexNum, 0.103 * vertexNum);
		thread t4(&DFS::FindCircle, &dfs4, 0.103 * vertexNum, 0.15 * vertexNum);
		thread t5(&DFS::FindCircle, &dfs5, 0.15 * vertexNum, 0.205 * vertexNum);
		thread t6(&DFS::FindCircle, &dfs6, 0.205 * vertexNum, 0.28 * vertexNum);
		thread t7(&DFS::FindCircle, &dfs7, 0.28 * vertexNum, 0.5 * vertexNum);
		thread t8(&DFS::FindCircle, &dfs8, 0.5 * vertexNum, vertexNum);

		if (t1.joinable()) { t1.join(); }
		if (t2.joinable()) { t2.join(); }
		if (t3.joinable()) { t3.join(); }
		if (t4.joinable()) { t4.join(); }
		if (t5.joinable()) { t5.join(); }
		if (t6.joinable()) { t6.join(); }
		if (t7.joinable()) { t7.join(); }
		if (t8.joinable()) { t8.join(); }


#ifdef TRACE_INFO
		dfs_time = clock();
		cout << "dfs time : " << (double)(dfs_time - cons_time) / CLOCKS_PER_SEC << "s" << endl;
#endif // TRACE_INFO


		MultiOutput(outputName);


#ifdef TRACE_INFO
		end_time = clock();
		cout << "output time : " << (double)(end_time - dfs_time) / CLOCKS_PER_SEC << "s" << endl;
		cout << "total time : " << (double)(end_time - start_time) / CLOCKS_PER_SEC << "s" << endl;
		cout << "total loops : " << total << endl;
		int ans[6];
		memset(ans, 0, 6 * 4);
		for (int i = 0; i < MAX_THREAD; ++i)
		{
			for (int j = 0; j < RES_LENGTH; ++j)
			{
				 ans[j] += g_ans[i][j].size;
			}
		}
		cout << "ans num : " << ans[0] << " " << ans[1] << " " << ans[2] << " " << ans[3] << " " << ans[4] << " " << ans[5] << endl;
#endif // TRACE_INFO
	}
	else
	{
#ifdef TRACE_INFO
		dfs_time = clock();
		cout << "dfs time : " << (double)(dfs_time - cons_time) / CLOCKS_PER_SEC << "s" << endl;
#endif // TRACE_INFO

		DFS single_dfs(0);
		single_dfs.FindCircle(0, vertexNum);
		MultiOutput(outputName);

#ifdef TRACE_INFO
		end_time = clock();
		cout << "output time : " << (double)(end_time - dfs_time) / CLOCKS_PER_SEC << "s" << endl;
		cout << "total time : " << (double)(end_time - start_time) / CLOCKS_PER_SEC << "s" << endl;
		cout << "total loops : " << total << endl;
#endif // TRACE_INFO
	}
	


	return 0;
}

你可能感兴趣的:(C/C++)