华为软挑 2020 4_17 浮生日记

简介

本人菜鸡一枚,但是对于顶级coder也是很向往的,今年因为个人的原因可能不能走的很远。但是还是想多学习一下
先从网上的代码分析开始好了

参考链接

参考链接 https://zhuanlan.zhihu.com/p/125764650

题目

题意:给定一个有向图,求出图中所有长度在[3,7]之间的环。
输入:格式为[IDU,IDV,~]的边表,ID为32位无符号整数(当然题目说明了,小于2^31,所以int32就好),边最多28W条,不重复,结点平均度数小于10。环中同一个ID不可以重复出现(若大环包括小环,则需要分开统计),环的个数不大于300W。
输出:环的个数,按照1.环长度;2.环的数字字典序输出所有环。

step by step 解析

Solution solution;// 构建一个解决方案类
solution.parseInput(testFile); // 读取数据将 id1,id2,money id1和id2 存储在 std::vector inputs;
solution.constructGraph();// 构建图
solution.solve();// 进行dfs查找环路,并存储结构
solution.save(outputFile);// 存储结构

源代码

https://paste.ubuntu.com/p/3gBwyNbw2w/

#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <fstream>
#include <time.h>
#define TEST
#define _CRT_SECURE_NO_WARNINGS 
// 防止fopen 和 fscanf 报错

struct Path {
	//ID最小的第一个输出;
	//总体按照循环转账路径长度升序排序;
	//同一级别的路径长度下循环转账账号ID序列,按照字典序(ID转为无符号整数后)升序排序
	int length;
	std::vector<unsigned int> path;

	Path(int length, const std::vector<unsigned int> &path) : length(length), path(path) {}

	bool operator<(const Path&rhs)const {//在排序的时候有用
		if (length != rhs.length) return length < rhs.length; // length 从小到大
		for (int i = 0; i < length; i++) {
			if (path[i] != rhs.path[i])// 字典序
				return path[i] < rhs.path[i];
		}
	}
};

class Solution {
public:
	//maxN=560000
	//maxE=280000 ~avgN=26000
	//vector<int> *G;
	std::vector<std::vector<int>> G;// 定义二维图
	std::unordered_map<unsigned int, int> idHash; //sorted id to 0...n
	std::vector<unsigned int> ids; //0...n to sorted id
	std::vector<unsigned int> inputs; //u-v pairs  先存储账号A 再存储账号B
	std::vector<int> inDegrees;
	std::vector<bool> vis;
	std::vector<Path> ans;
	int nodeCnt;

	void parseInput(std::string &testFile) {
		FILE* file = fopen(testFile.c_str(), "r");
		unsigned int u, v, c;
		int cnt = 0;
		while (fscanf(file, "%u,%u,%u", &u, &v, &c) != EOF) {
			inputs.push_back(u);
			inputs.push_back(v);
			++cnt;
		}
#ifdef TEST
		printf("%d Records in Total\n", cnt);// 输出总共存储了多少条数据
#endif
	}

	void constructGraph() {
		auto tmp = inputs;
		sort(tmp.begin(), tmp.end());// 对所有id 进行排序是什么意思??
#ifdef TEST
		std::cout << "开始输出数据" << tmp.size() << std::endl;
		for (int i = 0; i < 10; i++) {
			std::cout << tmp[i] << std::endl;
		}
		std::cout << "结束输出数据" << std::endl;
#endif
		tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end());//独一无二的tmp了
#ifdef TEST
		std::cout << "开始输出数据" << tmp.size() << std::endl;
		for (int i = 0; i < 10; i++) {
			std::cout << tmp[i] << std::endl;
		}
		std::cout << "结束输出数据" << std::endl;
#endif
		nodeCnt = tmp.size();
		ids = tmp;
		nodeCnt = 0;
		for (unsigned int &x : tmp) {
			idHash[x] = nodeCnt++;//map 中存储对应的账号id 所映射的位置???
		}
#ifdef TEST
		printf("%d Nodes in Total\n", nodeCnt);
#endif
		int sz = inputs.size();
		//G=new vector<int>[nodeCnt];
		G = std::vector<std::vector<int>>(nodeCnt);
		inDegrees = std::vector<int>(nodeCnt, 0);// 对于每一个节点的入度
		for (int i = 0; i < sz; i += 2) {
			int u = idHash[inputs[i]], v = idHash[inputs[i + 1]];
			G[u].push_back(v);//u节点指向v节点
			++inDegrees[v];// v节点的入度增加
		}
	}

	void dfs(int head, int cur, int depth, std::vector<int> &path) {
		vis[cur] = true;//当前节点已经访问过
		path.push_back(cur);//路径中存入当前节点
		for (int &v : G[cur]) {
			if (v == head && depth >= 3 && depth <= 7) { // 如果当前节点的一个出度是头结点,那么就是一个答案
				std::vector<unsigned int> tmp;
				for (int &x : path)
					tmp.push_back(ids[x]);
				ans.emplace_back(Path(depth, tmp));//等于push_back 不会触发 不需要触发拷贝构造和转移构造
			}
			if (depth<7 && !vis[v] && v>head) {
				dfs(head, v, depth + 1, path);
			}
		}
		vis[cur] = false;
		path.pop_back();
	}

	//search from 0...n
	//由于要求id最小的在前,因此搜索的全过程中不考虑比起点id更小的节点
	void solve() {
		vis = std::vector<bool>(nodeCnt, false);
		std::vector<int> path;
		for (int i = 0; i < nodeCnt; i++) {
			if (i % 100 == 0)
				std::cout << i << "/" << nodeCnt << std::endl;//每100个节点输出一个字符
			if (!G[i].empty()) {// 如果节点i对应的节点 有出度的话开启这个节点的dfs
				dfs(i, i, 1, path);
			}
		}
		sort(ans.begin(), ans.end());
	}

	void save(std::string &outputFile) {
		printf("Total Loops %d\n", (int)ans.size());
		std::ofstream out(outputFile);
		out << ans.size() << std::endl;
		for (auto &x : ans) {
			auto path = x.path;
			int sz = path.size();
			out << path[0];
			for (int i = 1; i < sz; i++)
				out << "," << path[i];
			out << std::endl;
		}
	}
};

int main()
{
	std::string testFile = "C:/Users/lee/Desktop/code/华为软件精英挑战赛/初赛/初赛赛题/test_data.txt";
	std::string outputFile = "C:/Users/lee/Desktop/code/华为软件精英挑战赛/初赛/初赛赛题/result_data.txt";
#ifdef TEST
	std::string answerFile = "C:/Users/lee/Desktop/code/华为软件精英挑战赛/初赛/初赛赛题/result.txt";
#endif
	auto t = clock();
	//    for(int i=0;i<100;i++){
	Solution solution;// 构建一个解决方案类
	solution.parseInput(testFile); // 读取数据将 id1,id2,money  id1和id2 存储在 std::vector<unsigned int> inputs; 
	solution.constructGraph();// 构建图
	//solution.topoSort();
	solution.solve();//解决它
	solution.save(outputFile);
	std::cout << clock() - t << std::endl;
	//    }
	system("pause");
	return 0;
}

输出结果分析

test_data.txt 中三个数据

18,197,396
197,56,420
56,18,378
构成了一个有三个节点参与的环
18 -> 197 -> 56 ---> 18

猜你喜欢

转载自www.cnblogs.com/eat-too-much/p/12719698.html