算法 {CPP在算法中的应用}

算法 {CPP在算法中的应用}
@LOC: 2

namespace和class的不同优劣点

场景1: 你需要一个很大的数组 int A[ 1003][ 1003]:
@IF(class): 这很不好处理 因为class obj; 这个对象是在栈空间里的 这容易爆栈!
. 解决办法: 你需要写成int (* A)[ 1003] 然后动态开内存 可是 你又需要多存储很多的指针 这种办法不好);
@IF(namespace) 他一定是在全局堆空间 这很好;

场景2: 必须要强行进行一个初始化操作(比如数位DP 他在使用之前 必须先预处理出来DP 即必须进行初始化)
@IF(class): 这很方便 直接放到构造函数即可;
@IF(namespace): 不太方便 你必须牢记着 手动的去调用一个全局函数;
. 解决办法: 在初始化函数那 写上文字(请手动调用) 让程序一开始编译出错, 然后用户就可以知道了;

类对象的全局初始化

一般来说 不要在全局初始化类对象, 而是在函数里去初始化, 因此:

class Solution{
	Graph * G;
	// Graph G = Graph( 1e5);
	
	Solution(){
		G = new Graph( 1e5);
	}
}

这是很正规的写法;

如果非要在全局初始化 也可以, 但你直接写成Graph G( 1e5); 这是错误的!(Error: expected parameter declarator)
而是要写成Graph G = Graph( 1e5); 这是规定…

不指定个数的 多组数据录入

if( false == (bool)( cin>> N)) 等价于 if( EOF != scanf("%d", &N));

move来使用vector的移动构造函数

@MARK: @LOC_1;
当前有一个vector< ST> A( 10) (他里面有10个ST对象), 现在要把他赋值给一个另一个vector<ST> B; 即进行B = A;操作;

如果你直接使用B = A, 那么他会调用vector拷贝赋值运算符operator=( const vector &);
. 从本质讲, 一个vector 他里面有一个ST * arr的动态申请的内存, 此时A里面的arr 和 B里面的arr, 两者是没有任何关系的 也就是一共是有20个ST大小的内存;

假如说, 你执行完B = A操作后, 永远也不会再使用A (这点非常重要), 换句话说, 你执行完B = A后 可以想象成 你主动调用了~A() 把他给析构掉了;
那么这种情况, 你可以使用B = std::move( A)来替代 通常的B = A, 那么他会调用vector移动赋值运算符operator=( ST &&);
. 简单讲 他会执行: B.arr = A.arr, A.arr = nullptr, 因此 不像深拷贝那样 (把A.arr的每个元素 赋值B.arr的每个元素), 而是浅拷贝 很简单 就把B的指针 直接指向A的指针;

即, 拷贝赋值(包括拷贝构造) 他是深拷贝, 两个对象 只是数据相同 但他们的内存地址 是完全不同的;
移动赋值(包括移动构造) 他是浅拷贝 , 两个对象 他们的内存地址 是指向同一地址的;

tuple,vector的排序规则

pair一样, 先比较<0>, 如果相同则 然后比较<1>, 以此类推;

cout输出浮点数 (precision, setf( ios::fixed))

执行一次操作: cout.setf( ios::fixed), cout.precision( 3);

以后调用cout<< 123.45555 会输出123.456;

@DELI;

cin.precision( 3)没有作用, 他并不说 控制录入的double就3位精度 不是的, 好像他什么作用也没有…
他不会影响cout.precision()所设置的 输出的精度;

__builtin_popcount

int __builtin_popcount( unsigned int)

注意你只能放int类型, 不可以是long long;

而且, 他实际上是获取 这块内存里 1的个数, 他并不关注符号 他关注的是二进制编码; 比如 -1 他的二进制编码为111...11, 所以他的结果为32;

使用static实现DFS的记忆化

MARK: @LOC_0;

T Dfs( int _cur){
	static bool __is_first = true;
	static T __record[ 100005];
	if( __is_first){
		__is_first = false;
		memset( __record, -1, sizeof( __record));
	}
	if( __record[ _cur] != -1){ return __record[ _cur];}
	
	return __record[ cur] = ?;
}

@DELI;

请注意, 如果是LeetCode 以上是错误的, 因为力扣的多组数据 只是入口函数的多次调用而已, 因此, 在力扣 你需要将__is_first修改到全局变量 然后在入口函数将他置为true, 如下示例:

bool __is_first;
T Dfs( int _cur){
	static T __record[ 100005];
	if( __is_first){
		__is_first = false;
		memset( __record, -1, sizeof( __record));
	}
	if( __record[ _cur] != -1){ return __record[ _cur];}
	
	return __record[ cur] = ?;
}

? `力扣的入口函数`(){
	__is_first = true;
	...
}

fill, iota

fill( A.begin(), A.end(), K) 将容器的每个元素赋值为K;

iota( A.begin(), A.end(), K) 将容器的每个元素赋值为K, K+1, K+2, ... (内部是通过不断执行++K来赋值的);
. iota 不是缩写, 他本身就是个单词 (表示一个极小的量);

sqrt获取下取整的平方根

long long c = sqrt( a)可以获取 任何>=0long long a的 平方根;
且满足: c*c <= a && (c+1)*(c+1)>a;

手写数组 来替代 容器queue/stack

T arr[ ?]; int head = 0, tail = 0;可以替代queue;
T arr[ ?]; int tail = 0; 可以替代stack;

手写的数组, 比系统的容器 效率要高;

全局数组 与 vector的效率

int A[ 1003]; // 方式1

int main(){
	vector< int> B(n); // 方式2
	//--
	...
}

使用全局数组(即方式1), 要比 方式2的vector, 快一倍; 因此要尽量避免使用vector;

ios::sync_with_stdio( false), cin.tie( NULL)的使用须知

此时, 要么所以的输入 都用cin, 要么都用scanf, 不可以两个都混用;
输出也是, 要么都用cout 要么都用printf, 不可以两个都混用;

找到数组中所有 X X X元素的位置

当然你可以使用个 O ( n ) O(n) O(n)的for循环来实现, 这里使用系统的函数find

**数组**

constexpr int N = 6;
int A[ N] = { 1, 2, 3, 1, 2, 3};
int X = 3;
for( auto it = find( A, A + N, X); it != A + N; it = find( it + 1, A + N, X)){
	
}

@Delimiter

**vector**

vector< int> A{ 1, 2, 3, 1, 2, 3};
int X = 3;
for( auto it = find( A.begin(), A.end(), X); it != A.end(); it = find( it + 1, A.end(), X)){
}

删除set中的一个连续子段

删除set中所有处在[L, R]区间内的元素;
. 比如set{1, 3, 5, 7, 9}, L = 4, R = 8, 则你需要将5,7删除掉;

(可以每次调用lower_bound(L)来删除, 但时间复杂度是O(k * log(n)) 其中k是要删除的元素个数; 我们有更优的做法)

因为set是有序的 我们将其看成是一个数列, 那么要删除的元素 会是该数列中的一个连续子段;
. 比如 [ a , b , X , Y , Z , c , d ] [a, b, X, Y, Z, c, d] [a,b,X,Y,Z,c,d] 其中 [ X , Y , Z ] [X,Y,Z] [X,Y,Z]就是要删除的连续子段;
. 使用iter = lower_bound( L)可以定位到 X X X;
. 此时用到一个操作 iter = set.erase( iter), 他会删除 X X X 且将其下一位指针作为返回值 即将 Y Y Y返回;
. . (注意erase(值)erase(迭代器) 是不同的, 他俩的返回值也不同);

for( auto iter = S.lower_bound( l); iter != S.end() && *iter <= r; iter = S.erase( iter)){
}

对拍程序

compare.cpp

ASSERT_( system("generate_data.exe > data.in") == 0);
ASSERT_( system("supimo.exe < data.in > supimo.out") == 0);
ASSERT_( system("correct.exe < data.in > correct.out") == 0);
ASSERT_( system("fc  supimo.out  correct.out") == 0);

这个对拍程序, 一次生成后 得到compare.exe, 以后就再不需要动了;

因为以后修改的, 就是generate_data, supimo, correct.cpp 这三个文件, 只要这三个程序.exe更新了, 就直接调用compare.exe即可;

system()函数是调用命令行, 相当于你在命令行的执行; 当执行没有问题 返回0;

fc是一个命令行里的语法 用于判定两个文件是否相同;

数组元素去重

数组

int A[N];
sort( A, A + N); // 或从大到小排序, 只要单调即可;
N = unique( A, A + N) - A;
//>< 此时, N已经改变, A[0,1,...,N)为N个不同的元素 也是单调的 (若之前A是升序 则现在前N个元素也是升序);

--

vector

vector< int> A(N);
sort( A.begin(), A.end()); // 或从大到小排序, 只要单调即可;
A.erase( unique( A.begin(), A.end()), A.end());
//>< A里面为不同的元素 也是单调的 (若之前A是升序 则现在前N个元素也是升序);

assert的报警提示

For assert( succ), when succ = false the assert would be happened;

If we wanna add some hints to that, we can use assert( succ && "some hints");

++自加/自减

连续使用
++ ++ a; is valid, for example, s.erase( ++ ++ it) means deleting the Second-Element that behind it;

@Delimiter

可应用于临时对象
s.erase( ++ it.begin()); which would produce a new Temporary-Iterator it that behinds it.begin();

set,map的迭代器

The iterator of set, multiset, map, multimap are Bidirectional (support only ++ or --)

s.erase( ++ s.begin());: delete the Second-Element;

反向迭代器转化为一般迭代器

The parameter of erase must be iterator not reverse_iterator, so if we wanna delete a Reverse-Iterator (e.g., r_it = rbegin())), the operation is s.erase( (++ r_it).base());

cerr调试输出,各算法平台的调试机制

cerr << 12; cout<< 34;

In our IDE-environment: both 12, 34 would be outputted, visually, no distinction between them;

In Online-Judge (except LeetCode), 12 would not affect the answer (only cout would be viewed as answer)
. For example, in AcWing, all cout would be showed if you have no cerr; otherwise, only cerr would be showed and no cout is showed once you have cerr;

In LeetCode, all cerr are forbidden, only cout would be showed (and also cout would not affect the answer which is only specified by the return-variable of function)

整型最大最小值INT32_MAX

INT32_MAX, INT32_MIN; UINT32_MAX
INT64_MAX, INT64_MIN; UINT64_MAX

使用自定义类型来替代tuple, pair

struct ST{
	T1 t1;
	T2 t2;
	T3 t3;
};

is always better than tuple< T1, T2, T3> and pair< T1, pair< T2, T3>>

next_permutation

vector< int> Perm{ 0, 1, 2, 3};
do{
	Perm is `0123` `0132` `0213` `0231` `0312` `0321` `1023` ...
}while( next_permutation( Perm.begin(), Perm.end()));

Make sure the initial value of Perm must be {0, 1, 2, 3, ...}.

stringstream

stringstream will get all sub-strings divided by a sequence of space( )

s = " 1 2    3    ";

stringstream ss( s);
string a;
while( ss >> a){
	a = "1", "2", "3";
}

if all sub-string are int, string a can be written to int a;

避免使用匿名函数

It is always better to using a Normal-Function than a Anonymous-Function;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值