华为软挑 2020 4_19 浮生日记

简介

还是强调一下,本人菜鸡一枚,但是对于顶级coder也是很向往的,今年因为个人的原因可能不能走的很远。但是还是想多学习一下。以下代码来自 --大学 同学,杨菲菲

提升思路

加速 IO
多线程
减少内存的申请和释放
更新算法

code

/*
 * @Author: your name
 * @Date: 2020-04-17 10:43:11
 * @LastEditTime: 2020-04-17 10:43:42
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: codelesson_1.cpp
 */

#define _LINUX

#include <iostream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <fstream>
#include <thread>
#include <cstring>
#include <time.h>

#ifdef _LINUX
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#endif

#define _CRT_SECURE_NO_WARNINGS  // 防止 fopen 和 fscanf 报错

static bool vis[600000];
static int Gout[600000][100];    // 记录出边, Gout[i][j]表示结点i第j条出边所连接的结点 
static int Gin[600000][500];     // 记录入边, Gin[i][j]表示结点i第j条入边所连接的结点 
static int pOut[600000];         // 每个结点出边索引j 
static int pIn[600000];          // 每个结点入边索引

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:
    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 
    std::vector<int> path;
    std::vector<Path> ans;
    int nodeCnt;
    
#ifdef _LINUX
    void parseInput(std::string &testFile)
    {
        int fd = open(testFile.c_str(), O_RDONLY);
        int len = lseek(fd, 0, SEEK_END);
        char *mbuf = (char *)mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
        if(fd != -1) {
            close(fd);  
        }
        unsigned int offset = 0, cnt = 0;;
        unsigned int u, v, c;
        for(int i = 0; i < len; i++) {
            if(mbuf[i] == '
') {
                mbuf[i] = '';
                sscanf(mbuf + offset, "%u,%u,%u", &u, &v, &c);
                offset = i + 2;   // 跳过 /r/n 
                inputs.push_back(u);
                inputs.push_back(v);
                ++cnt;
            }
        }
        munmap(mbuf, len); 
    }
#else
    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;
        }
        if(file != NULL) {
            fclose(file);
        }
        
#ifdef TEST
        printf("%d Records in Total
", cnt);  // 输出总共存储了多少条数据
#endif

    }
#endif

    void constructGraph() 
    {
        std::vector<unsigned int> tmp = inputs;
        sort(tmp.begin(), tmp.end()); // 对所有id进行排序
        tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end()); // 独一无二的tmp了
        ids = tmp;
        nodeCnt = 0;
        
        for(int i = 0; i < tmp.size(); i++) {
            idHash[tmp[i]] = nodeCnt++;
        }
        
#ifdef TEST
        printf("%d Nodes in Total
", nodeCnt);
#endif

        int sz = inputs.size();
        memset(pOut, 0, sizeof(pOut));
		memset(pIn, 0, sizeof(pIn));
        for (int i = 0; i < sz; i += 2) {
            int u = idHash[inputs[i]], v = idHash[inputs[i + 1]];
            Gout[u][pOut[u]++] = v;
            Gin[v][pIn[v]++] = u;
        }
    }

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

    // search from 0...n
    // 由于要求id最小的在前,因此搜索的全过程中不考虑比起点id更小的节点
    void solve() 
    {
        for (int i = 0; i < nodeCnt; i++) {
            if (pOut[i] && pIn[i]) { // 如果节点i对应的节点 有出度的话开启这个节点的dfs
                dfs(i, i, 1);
            }
        }
        sort(ans.begin(), ans.end());
    }

    void save(std::string &outputFile)
    {
        std::ostringstream os;
        os << ans.size() << std::endl;
        
        for(int k = 0; k < ans.size(); k++) {
            std::vector<unsigned int> path = ans[k].path;
            int sz = path.size();
            os << path[0];
            for (int i = 1; i < sz; i++) {
                os << "," << path[i];
            }
            os << std::endl;
        }
        FILE * fp = fopen(outputFile.c_str(), "w" );
        fwrite(os.str().c_str(), os.str().length(), 1, fp);
        fclose(fp);
    }
};

int main()
{
    std::string testFile = "/data/test_data.txt";
    std::string outputFile = "/projects/student/result.txt";
    
#ifdef TEST
    std::string answerFile = "C:/Users/lee/Desktop/code/华为软件精英挑战赛/初赛/初赛赛题/result.txt";
#endif

    Solution solution;             // 构建一个解决方案类
    solution.parseInput(testFile); // 读取数据将 id1,id2,money  id1和id2 存储在 std::vector<unsigned int> inputs; 
    solution.constructGraph();     // 构建图
    //solution.topoSort();
    solution.solve();              //解决它
    solution.save(outputFile);
    return 0;
}

分数

5.5

结论

内存的申请和释放占据了大量时间。读取数据和写入数据 采用的mmap可以加速数据的读取和写入

Hope is a good thing,maybe the best of things,and no good thing ever dies.----------- Andy Dufresne
原文地址:https://www.cnblogs.com/eat-too-much/p/12734559.html