昨天校赛一塌糊涂。我和我的队友都已经进入更年期了。^_^
比赛中这是个简单题,一看到题目我就想到可以写,而且很简单。开始想到的是用STL的list很简单的去写,但list每次删除和添加元素之后迭代器指在什么地方不太清楚,于是就手写链表。结果TLE了,想到TLE只有可能是分配内存和释放内存的问题,于是改用数组模拟了链表,通过。今天看到大家说起list的效率,也看到哑熊(Dumbear)用list过了这个题,于是就默写了昨天比赛的代码,去进行了测试,结果发现我的程序在时间和内存上都略有优势。代码如下:
我的代码:
/* * Author: stormdpzh * Created Time: 2013/4/22 19:27:49 * File Name: woj_1478.cpp */ #include <iostream> #include <cstdio> #include <sstream> #include <cstring> #include <string> #include <cmath> #include <vector> #include <queue> #include <stack> #include <map> #include <set> #include <list> #include <algorithm> #include <functional> #define sz(v) ((int)(v).size()) #define rep(i, n) for(int i = 0; i < n; i++) #define repf(i, a, b) for(int i = a; i <= b; i++) #define repd(i, a, b) for(int i = a; i >= b; i--) #define out(n) printf("%d\n", n) #define mset(a, b) memset(a, b, sizeof(a) using namespace std; typedef long long lint; const int INF = 1 << 30; const int MaxN = 1000015; char s[MaxN]; int pre[MaxN]; int next[MaxN]; void gao() { int len = strlen(s + 1); repf(i, 0, len) { pre[i] = next[i] = -1; } int id = 0; repf(i, 1, len) { if(s[i] == '-') { if(id != 0) { int t1 = pre[id], t2 = next[id]; next[t1] = t2; if(t2 != -1) pre[t2] = t1; id = t1; } } else if(s[i] == '<') { if(pre[id] != -1) id = pre[id]; } else if(s[i] == '>') { if(next[id] != -1) id = next[id]; } else { int t = next[id]; next[id] = i; pre[i] = id; next[i] = t; if(t != -1) pre[t] = i; id = i; } } } int main() { int t; scanf("%d", &t); repf(cas, 1, t) { scanf("%s", s + 1); gao(); int id = next[0]; printf("Case %d: ", cas); while(id != -1) { printf("%c", s[id]); id = next[id]; } puts(""); } return 0; }哑熊(Dumbear)的代码:
#include <cstdio> #include <string> #include <list> using namespace std; int t; list<char> text; list<char>::iterator cursor; void process(char c) { if (c == '-') { if (cursor != text.begin()) { --cursor; cursor = text.erase(cursor); } } else if (c == '<') { if (cursor != text.begin()) --cursor; } else if (c == '>') { if (cursor != text.end()) ++cursor; } else { cursor = text.insert(cursor, c); ++cursor; } } void solve() { text.clear(); cursor = text.begin(); char c; while ((c = getchar()) != '\n') process(c); string s; for (list<char>::iterator i = text.begin(); i != text.end(); ++i) s += *i; printf("Case %d: %s\n", ++t, s.c_str()); } int main() { int t; scanf("%d", &t); while (getchar() != '\n'); for (int i = 0; i < t; ++i) solve(); return 0; }这不禁引起了我的思考:list在时间上和空间上的不足在哪里?
首先,我的程序在时间上是完全O(1)的,每个操作都是O(1)的完成,而list肯定会涉及到内存的分配回收,这肯定会有时间的浪费。但有一点:list肯定不会像手写的链表那样每次删除元素就立马释放内存,不然也可能出现像昨天那样的TLE的问题。
其次,list的空间也比数组占用多。注意到我是有三个数组的,所以list应该其实是多用了不少内存的。但由于list不要求内存的连续性,所以它肯定不会像vector那样出现在申请内存时的浪费(vector为了提高时间效率,在连续的内存空间不够用时,会重新开辟出当前两倍的空间,具体参见 http://blog.csdn.net/stormdpzh/article/details/8727509),这个浪费更可能是由于它像vector那样,类似的,在删除元素时没有立马释放内存。这样就解释了为什么它比手写的链表快很多。
另外,本人进行了一个测试,发现vector和list相比,如果不断的push_back,即只在最后加入元素的话,在元素较少时,vector比list快,而在元素增多时,list的速度会越来越有优势。这个其实也是可以理解的:vector在元素较少时,不会像list那样插入一个元素都要重新申请空间,而他们的插入又都不会有元素的移动,所以vector占优;而在元素增多时,vector一旦出现连续内存不足的情况,就得重新申请空间并移动所有元素,这势必会降低它的效率。
ps:以上很多内容有很大推测成分,欢迎拍砖。
总结:STL在做ACM比赛的题目时,恰当的应用肯定会有很大的好处,但是这种应用可能也会导致基本功的不扎实,比如sort用多了连快排都不能马上写出来了。所以,在用STL的同时要尽量深入理解,本人在前面的面试中也深刻感受到了这个问题。
again:进入更年期的男人。