昨天校赛一塌糊涂。我和我的队友都已经进入更年期了。^_^
比赛中这是个简单题,一看到题目我就想到可以写,而且很简单。开始想到的是用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:进入更年期的男人。