我有一个二维数组,列数固定为4,但行数非常大。我发现使用vector<tuple>
而不是vector<array>
或vector<vector>
可以快近两倍,比后者快五倍。虽然使用tuple
有点麻烦,但我定义了一个访问函数来解决这个问题:
typedef tuple<int,int,int,int> Tuple;
// access the i-th element
inline int &tget (Tuple &t, int i) {
switch (i) {
case 0: return std::get<0>(t);
case 1: return std::get<1>(t);
case 2: return std::get<2>(t);
case 3: return std::get<3>(t);
}
}
vector<Tuple> array;
// this gives the element in i-th row and j-th column
tget(array[i],j);
我觉得这非常令人惊讶,想知道这种性能提升来自哪里,是否存在一种类似于
vector<vector>
或vector<array>
的简洁解决方案,使用下标operator[]
,并具有与tuple
相同的性能?
更新:
我正在使用带有标准的,并使用-O3
进行优化。
因为有人问操作,所以在此进行解释。首先,2D数组被填充了多达1M行。首先我会预留空间,然后使用push_back
函数添加元素。然后我对数组进行排序,最后在其中进行二进制搜索。这是我编写的代码块。输入将是几个测试用例(=T),每个测试用例都需要一个整数(N最大为20),然后再读取N个整数到中。由于该函数具有指数特性,因此总和变得非常大(4^(N/2) 最高约一百万)。要在数组和元组之间进行测试,请切换typedef并注释/取消注释访问函数中的返回行。有四行/块必须在
// if array
// if tuple
注释中进行切换,并且注释指定在注释中。#include <iostream>
#include <algorithm>
#include <vector>
#include <tuple>
#include <array>
using namespace std;
typedef vector<int>::iterator VecIt;
// HERE you can switch between the two types
typedef tuple<int,int,int,int> Tuple; // define it as a tuple
//typedef array<int,4> Tuple; // define it as array
inline int &tget (Tuple &t, int i) {
// return t[i]; // if it's an array, uncomment This too
// if it's an array comment the switch case
switch (i) {
case 0: return std::get<0>(t);
case 1: return std::get<1>(t);
case 2: return std::get<2>(t);
case 3: return std::get<3>(t);
default:
cerr << "tuple index out of bound" << endl;
}
}
void comp_sums(VecIt v, VecIt vend, vector<Tuple> &sums){
if (v==vend)
return;
int size = sums.size();
for (int i=0; i<size; i++) {
for (int j=1; j<4; j++){
sums.push_back(sums[i]);
tget(sums.back(),j) += *v;
}
tget(sums[i],0) += *v;
}
v++;
comp_sums(v, vend, sums);
}
void docase( ) {
int N;
cin >> N;
vector<int> lens(N);
int lsum = 0;
for (int i=0; i<N; i++) {
cin >> lens[i];
lsum += lens[i];
}
if (lsum % 4 != 0 ) {
cout << 0 << endl;
return;
}
lsum = lsum/4;
vector<Tuple> sums1, sums2;
Tuple z(0,0,0,0); // if it's a tuple
//Tuple z = {0,0,0,0}; // If it's an array
sums1.push_back(z); sums1.reserve(1<<N/2);
sums2.push_back(z); sums2.reserve(1<<N/2);
comp_sums(lens.begin()+1, lens.begin()+N/2, sums1);
comp_sums(lens.begin()+N/2+1, lens.end(), sums2);
sort(sums1.begin(), sums1.end());
sort(sums2.begin(), sums2.end());
long count = 0;
for (int i=0; i<sums1.size(); i++) {
for (int k=0; k<4; k++) {
//Tuple t({lsum,lsum,lsum,lsum}); // if it's an array
Tuple t(lsum,lsum,lsum,lsum); // if it's a tuple
for (int j=0; j<4; j++)
tget(t,j) -= tget(sums1[i],(j+k)%4);
tget(t,k) -= lens[0];
tget(t,0) -= lens[N/2];
bool neg = false;
for (int j=0; j<4; j++)
if (tget(t,j)<0){
neg = true;
break;
}
if (neg)
continue;
auto LB = lower_bound(sums2.begin(), sums2.end(), t);
auto UB = upper_bound(LB, sums2.end(), t);
count += (UB - LB);
}
}
cout << count/6 << endl;
}
int main() {
std::ios_base::sync_with_stdio(false);
int T;
cin >> T;
while (T--) docase();
}
这里还有一个我使用的测试输入。第一行表示有T = 18个不同的测试用例。每个测试用例的第一行是N(最多20),然后是N个整数。算法呈指数增长,因此小数字并不意味着只有几个操作。在测量时间时,我已经考虑了I/O:
18
8
132391 123854 21536 19482 133025 10945 121800 10311
8
12 4 12 4 4 12 12 24
8
131723 16253 23309 132227 125171 12253 16757 136227
8
8 4 4 8 4 8 12 24
8
12 12 12 8 4 8 12 28
8
115021 114654 112093 17443 20371 17810 17274 115190
8
8 8 4 4 12 4 12 20
8
12 4 4 4 4 12 12 28
8
8 12 8 12 8 4 12 28
8
4 12 8 12 8 8 4 24
20
34836 56869 24112 27796 198091 196472 50364 27364 29572 53701 52981 25779 202644 204884 53840 30056 48705 53092 43751 18419
20
207447 26867 41968 35977 36384 57042 40981 30191 47705 26907 248361 26003 53715 48237 27691 192772 60507 58194 20754 245913
20
34836 56869 24112 27796 198091 196472 50364 27364 29572 53701 52981 25779 202644 204884 53840 30056 48705 53092 43751 18419
20
207447 26867 41968 35977 36384 57042 40981 30191 47705 26907 248361 26003 53715 48237 27691 192772 60507 58194 20754 245913
20
4 2 2 4 4 2 4 2 4 2 2 4 4 3 3 4 2 3 4 1
20
2 3 2 2 3 2 3 4 3 2 4 4 4 3 2 3 4 4 3 3
20
4 4 4 4 3 3 3 2 2 4 2 4 2 2 4 2 2 2 2 1
20
4 4 2 3 4 3 4 3 2 4 4 2 2 4 3 4 3 3 2 4