📜  竞争编程初学者的一些有用C ++技巧(1)

📅  最后修改于: 2023-12-03 15:27:22.703000             🧑  作者: Mango

竞争编程初学者的一些有用C++技巧

竞争编程是指,参赛者在规定的时间内(一般为数小时到数天),独立完成所给出的若干个问题(即“比赛题目”)的设计与实现,通过对竞赛系统的自动评测程序或人工评测进行测试,最终得到比赛成绩和排名的竞争形式。以下是一些可以帮助初学者在竞争编程中编写更高效、更简洁代码的C++技巧。

将头文件一次性包含

当写C++代码时,通常要引入一些标准头文件如<iostream>和专用头文件如<vector><string>。虽然可以在每个文件中都单独包含所需的头文件,但更好的方式是将所有必需的头文件一次性包含于一个公共文件中,如下所示:

#include <bits/stdc++.h>
using namespace std;

int main() {
    vector<int> v;
    v.push_back(1);
    v.push_back(2);

    cout << v[0] << " " << v[1] << endl;

    return 0;
}

当然,这种方法不够安全,因为<bits/stdc++.h>包含了所有标准头文件,可能会导致一些命名冲突。所以如果想保证代码的安全性,可以逐个包含所需的头文件。

使用typedefdefine来简化代码

C++中有许多长而复杂的数据类型,如std::map<int, std::pair<int, int>>。使用typedefdefine可以简化代码,使其更易于理解和编写,例如:

typedef std::map<int, std::pair<int, int>> MPP;

#define pii std::pair<int, int>
#define mp(a, b) std::make_pair(a, b)

这样,以后可以用MPP代替std::map<int, std::pair<int, int>>:

MPP myMap;
myMap[1] = mp(2, 3);
优先队列(堆)添加自定义比较方式

std::priority_queue是一个可以用于实现堆的STL容器,很多初学者在堆操作时可能会犯迷糊。我们可以在定义std::priority_queue时添加自定义的比较方式,比如:

struct node{
    int x, y;
} a[100001];

struct cmp{
    bool operator()(int i, int j) {
        return a[i].x > a[j].x;
    }
};

 std::priority_queue<int, std::vector<int>, cmp> pq;

这里的自定义比较是指按照a[i].x的从小到大顺序将节点插入堆中。

vector作为布尔类型数组的替代品

如果需要一个布尔类型数组,用std::vector<bool>bool arr[N]更省空间。虽然bool类型只需要1字节,但在使用bool arr[N]时,可能会占用更多的内存空间,例如:

const int maxn = 10000000;
bool prime[maxn];

int main() {
    memset(prime, true, sizeof(prime));  // 慢
    return 0;
}

相比之下,使用std::vector<bool>会更省空间:

const int maxn = 10000000;
std::vector<bool> prime(maxn, true);

int main() {
    return 0;
}
尽可能使用auto关键字

使用auto关键字可以自动推导出表达式的类型,从而简化代码。例如:

std::vector<int> a = {1, 2, 3};

for (auto i : a) {
    cout << i << " ";
}

这里可以用auto关键字自动推导变量类型,从而避免了需要手动指定一个类型的麻烦。为了保证代码的可读性,应该适量地使用auto关键字。

将循环变量定义在循环语句中

将循环变量定义在循环语句中可以提高代码的可读性和效率。例如,下面的代码:

for (int i = 0; i < n; ++i) {
    // ...
}

比这种代码:

int i;
for (i = 0; i < n; ++i) {
    // ...
}

更易于阅读和理解。另外,将循环变量定义在循环语句中还可以避免变量作用域和命名冲突的问题。

使用staticconst关键字

使用staticconst关键字可以提高代码的可读性和效率。static关键字可以在局部变量和函数中防止变量和函数被频繁创建和销毁,从而提高效率。const关键字可以在变量定义时防止变量被修改,从而增加代码的安全性和可读性。例如:

class Solution {
public:
    static const int MAXN = 100001;
    static const int MOD = 1000000007;
    static const double EPS = 1e-9;
    static const std::string STR;

    int add(int a, int b) {
        return (a + b) % MOD;
    }
};

const std::string Solution::STR = "Hello";
使用emplace_back()代替push_back()

std::vectorpush_back()函数会将元素复制到容器中,从而占用一定的时间和空间。而emplace_back()函数可以直接在容器中创建元素,从而避免了元素拷贝和内存传输,提高了效率。例如:

typedef std::pair<int, int> PII;

std::vector<PII> v;
v.emplace_back(1, 2);
cincout进行优化

读入和输出通常是程序中的一个瓶颈,所以优化输入输出可以提高程序的速度。有两种方法可以优化输入输出:一是关闭同步流,二是使用快速IO。例如:

// 关闭同步流
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);

// 使用快速IO
printf("%.6lf\n", ans);

可以通过关闭同步流、断开cin/cout的联系和使用printf()scanf()等函数来提高输入输出效率。但是,这种方法的代码可读性不如使用C++的IO流,建议在能够忍受一定时间的输入输出延迟的情况下,尽量避免使用这些快速输入输出方法。

以上是一些可以帮助竞争编程初学者编写更高效、更简洁代码的C++技巧。