May 7, 2008

字符串是程序中经常要表达和处理的数据,我们通常是采用字符数组或字符指针来表示字符串。STL为我们提供了另一种使用起来更为便捷的字符串的表达方式:string。string类的定义在头文件<string>中。

string类其实可以看作是一个字符的vector,vector上的各种操作都可以适用于string,另外,string类对象还支持字符串的拼合、转换等操作。

下面先来看一个简单的例子:

#include <iostream>
#include <string>
using namespace std;
main()
{
    string s = "Hello! ", name;
    cin >> name;
    s += name;
    s += '!';
    cout << s << endl;
    return 1;
}

再以题1064--Parencoding为例,看一段用string作为容器,实现由P代码还原括号字符串的示例代码片段:

int m;
cin >> m; // P编码的长度
string str; // 用来存放还原出来的括号字符串
int leftpa = 0; // 记录已出现的左括号的总数
for (int j=0; j<m; j++)
{
    int p;
    cin >> p;
    for (int k=0; k<p-leftpa; k++) str += '(';
    str += ')';
    leftpa = p;
}

STL的头文件中描述了一个看上去非常简单的模板类pair,用来表示一个二元组或元素对,并提供了按照字典序对元素对进行大小比较的比较运算符模板函数。

例如,想要定义一个对象表示一个平面坐标点,则可以:

pair p1;
cin >> p1.first >> p1.second;

pair模板类需要两个参数:首元素的数据类型和尾元素的数据类型。pair模板类对象有两个成员:first和second,分别表示首元素和尾元素。

中已经定义了pair上的六个比较运算符:<、>、<=、>=、==、!=,其规则是先比较first,first相等时再比较second,这符合大多数应用的逻辑。当然,也可以通过重载这几个运算符来重新指定自己的比较逻辑。

除了直接定义一个pair对象外,如果需要即时生成一个pair对象,也可以调用在中定义的一个模板函数:make_pair。make_pair需要两个参数,分别为元素对的首元素和尾元素。

在题1067--Ugly Numbers中,就可以用pair来表示推演树上的结点,用first表示结点的值,用second表示结点是由父结点乘以哪一个因子得到的。

#include
#include

using namespace std;

typedef pair node_type;

main()
{
unsigned long result[1500];
priority_queue< node_type, vector, greater > Q;
Q.push( make_pair(1, 2) );
for (int i=0; i<1500; i++)
{
node_type node = Q.top(); Q.pop();
switch(node.second)
{
case 2: Q.push( make_pair(node.first*2, 2) );
case 3: Q.push( make_pair(node.first*3, 3) );
case 5: Q.push( make_pair(node.first*5, 5) );
}
result[i] = node.first;
}

int n;
cin >> n;
while (n>0)
{
cout << result[n-1] << endl;
cin >> n;
}

return 1;
}

看上去是很简单的一个头文件,但是的设计中却浓缩反映了STL设计的基本思想。有意深入了解和研究STL的同学,仔细阅读和体会这个简单的头文件,不失为一种入门的途径。

在STL的<vector>头文件中定义了vector(向量容器模板类),vector容器以连续数组的方式存储元素序列,可以将vector看作是以顺序结构实现的线性表。当我们在程序中需要使用动态数组时,vector将会是理想的选择,vector可以在使用过程中动态地增长存储空间。

vector模板类需要两个模板参数,第一个参数是存储元素的数据类型,第二个参数是存储分配器的类型,其中第二个参数是可选的,如果不给出第二个参数,将使用默认的分配器。

下面给出几个常用的定义vector向量对象的方法示例:

vector<int> s;  
定义一个空的vector对象,存储的是int类型的元素。

vector<int> s(n);
定义一个含有n个int元素的vector对象。
vector<int> s(first, last);  
定义一个vector对象,并从由迭代器first和last定义的序列[first, last)中复制初值。

vector的基本操作有:

s[i]
直接以下标方式访问容器中的元素。

s.front()
返回首元素。
s.back()
返回尾元素。

s.push_back(x)
向表尾插入元素x。
s.size()
返回表长。

s.empty()
当表空时,返回真,否则返回假。
s.pop_back()
删除表尾元素。

s.begin()
返回指向首元素的随机存取迭代器。
s.end()
返回指向尾元素的下一个位置的随机存取迭代器。

s.insert(it, x)
向迭代器it指向的元素前插入新元素val。
s.insert(it, n, x)
向迭代器it指向的元素前插入n个x。

s.insert(it, first, last)
将由迭代器first和last所指定的序列[first, last)插入到迭代器it指向的元素前面。
s.erase(it)
删除由迭代器it所指向的元素。

s.erase(first, last)
删除由迭代器first和last所指定的序列[first, last)。
s.reserve(n)
预分配缓冲空间,使存储空间至少可容纳n个元素。

s.resize(n)
改变序列的长度,超出的元素将会被删除,如果序列需要扩展(原空间小于n),元素默认值将填满扩展出的空间。
s.resize(n, val)
改变序列的长度,超出的元素将会被删除,如果序列需要扩展(原空间小于n),将用val填满扩展出的空间。

s.clear()
删除容器中的所有的元素。
s.swap(v)
将s与另一个vector对象v进行交换。

s.assign(first, last)
将序列替换成由迭代器first和last所指定的序列[first, last)。[first, last)不能是原序列中的一部分。
要注意的是,resize操作和clear操作都是对表的有效元素进行的操作,但并不一定会改变缓冲空间的大小。

另外,vector还有其他一些操作如反转、取反等,不再一下列举。

vector上还定义了序列之间的比较操作运算符(>, <, >=, <=, ==, !=),可以按照字典序比较两个序列。

还是来看一些示例代码。输入个数不定的一组整数,再将这组整数按倒序输出,如下所示:

#include <iostream>
#include <vector>
using namespace std;
main()
{
    vector<int> L;
    int x;
    while (cin>>x) L.push_back(x);
    for (int i=L.size()-1; i>=0; i--) cout << L[i] << " ";
    cout << endl;
    return 1;
}

一、关于STL

STL(Standard Template Library,标准模板库)是C++语言标准中的重要组成部分。STL以模板类和模板函数的形式为程序员提供了各种数据结构和算法的精巧实现,程序员如果能够充分地利用STL,可以在代码空间、执行时间和编码效率上获得极大的好处。
STL大致可以分为三大类:算法(algorithm)、容器(container)、迭代器(iterator)。

STL容器是一些模板类,提供了多种组织数据的常用方法,例如vector(向量,类似于数组)、list(列表,类似于链表)、deque(双向队列)、set(集合)、map(映象)、stack(栈)、queue(队列)、priority_queue(优先队列)等,通过模板的参数我们可以指定容器中的元素类型。

STL算法是一些模板函数,提供了相当多的有用算法和操作,从简单如for_each(遍历)到复杂如stable_sort(稳定排序)。

STL迭代器是对C中的指针的一般化,用来将算法和容器联系起来。几乎所有的STL算法都是通过迭代器来存取元素序列进行工作的,而STL中的每一个容器也都定义了其本身所专有的迭代器,用以存取容器中的元素。有趣的是,普通的指针也可以像迭代器一样工作。

熟悉了STL后,你会发现,很多功能只需要用短短的几行就可以实现了。通过STL,我们可以构造出优雅而且高效的代码,甚至比你自己手工实现的代码效果还要好。

STL的另外一个特点是,它是以源码方式免费提供的,程序员不仅可以自由地使用这些代码,也可以学习其源码,甚至按照自己的需要去修改它。

下面是用STL写的题1067--Ugly Numbers的代码:

#include <iostream>
#include <queue>

using namespace std;

typedef pair<unsigned long, int> node_type;

main()
{
 unsigned long result[1500];
 priority_queue< node_type, vector<node_type>, greater<node_type> > Q;
 Q.push( make_pair(1, 2) );
 for (int i=0; i<1500; i++)
 {
  node_type node = Q.top(); Q.pop();
  switch(node.second)
  {
  case 2: Q.push( make_pair(node.first*2, 2) );
  case 3: Q.push( make_pair(node.first*3, 3) );
  case 5: Q.push( make_pair(node.first*5, 5) );
  }
  result[i] = node.first;
 }

 int n;
 cin >> n;
 while (n>0)
 {
  cout << result[n-1] << endl;
  cin >> n;
 }

 return 1;
}



二、使用STL

在C++标准中,STL被组织为以下的一组头文件(注意,是没有.h后缀的!):

algorithm / deque / functional / iterator / list / map

memory / numeric / queue / set / stack / utility / vector

当我们需要使用STL的某个功能时,需要嵌入相应的头文件。但要注意的是,在C++标准中,STL是被定义在std命名空间中的。如下例所示:

#include <stack>

main()

{

std::stack<int> s;

s.push(0);

...

return 1;

}

如果希望在程序中直接引用STL,也可以在嵌入头文件后,用using namespace语句将std命名空间导入。如下例所示:

#include <stack>

using namespace std;

main()

{

stack<int> s;

s.push(0);

...

return 1;

}

STL是C++语言机制运用的一个典范,通过学习STL可以更深刻地理解C++语言的思想和方法。在本系列的文章中不打算对STL做深入的剖析,而只是想介绍一些STL的基本应用。

在ACM竞赛中,熟练掌握和运用STL对快速编写实现代码会有极大的帮助。

一、ACM/ICPC竞赛的特点

ACM/ICPC(国际大学生程序设计竞赛)是以算法设计为主的程序设计竞赛,并不涉及具体的应用技术。

ACM/ICPC竞赛以组队形式参赛,每个参赛队由三名队员组成,共同使用一台计算机解题。通常每场比赛的试题为6至10题,根据各队的完成题数和罚时进行排名。题目提交通过称为完成,从比赛开始到提交成功所用的时间为题目的基础罚时,另外,一道题目每提交失败一次,将增加20分钟罚时。也就是说,参赛队要尽可能用最快的速度、最少的失败次数,解决最多的题目。

二、输入和输出处理
试题一般采用标准输入和输出方式读取输入和产生输出,在题目中会详细描述输入和输出的格式和值域范围,所写的程序一定要严格遵守题目指定的输入输出格式。

在比赛试题的输入和输出处理上,针对一些常见的情形,有一些常用的方法。

1、多测试用例的输入和输出

有些试题在一次输入中只包含一个测试用例,也就是说,程序每运行一次,只算一道题。也有些试题在一次输入中包含多个测试用例,也就是说,程序每运行一次,要计算多道题。

对多用例输入,通常会先输入要计算的用例的个数,然后依次输入每个测试用例的输入数据,但程序并不需要等到所有的测试用例都计算完后再输出所有测试用例的计算结果,而是可以读入一个测试用例,输出一个结果,再读入一个测试用例,再输出一个结果。因此对多用例输入的试题,可以用这样的输入模式:

以C++为例:

int n;

cin >> n;

for (int i=0; i

{

读入测试用例数据

计算

输出计算结果

}

2、单测试用例输入的结束判断
对单用例输入,最主要的问题是如何知道输入什么时候结束。

有些试题会指定某种特殊的输入值作为输入的结束标志,这种情况比较容易处理,只须在读入后,判断一下读入的内容是否为约定结束值即可。

有些试题并不指定特殊的输入值,而是以EOF(文件结束标志)作为结束标志。如果从文件流读入,当读到文件尾时,输入返回EOF。如果从键盘读入时,在Windows的终端中,是以Ctrl+Z表示EOF。对于这种情况,可以用这样的输入模式:

以C++为例:

int m, n; // 假设要连续输入一组整数对

while (cin>>m>>n)

{

处理整数对(m, n)

}

以C语言为例:

int m, n;

while (scanf("%d%d", &m, &n)==2)

{

处理整数对(m, n)

}

三、数据结构的设计
很多试题中已经给出了数据量的上限,因此可以很方便地以数组的方式定义数据结构。但也要注意到有些题目中没有明确指出数据上限时,切不可盲目定义数组大小。

例如在题1070(多项式求和)中,并未说明输入多项式的项数,对这种情况就不宜用数组方式来表示多项式了——除非你的运气足够好,所开辟的数组大小能够经受所有的测试用例的考验。

除了使用一般的数组或链表结构外,对使用C++的选手来说,STL也是一大利器,充分运用可以有效提高编程的效率和正确性。

四、测试用例的考虑
在试题中通常会给出测试用例的样例,这通常会被我们用来测试自己的程序,而且很多选手往往在正确计算出测试用例样例时,会认为自己的程序是正确的。

其实测试用例的样例只是测试用例的个例,实际用于测试的测试用例往往会涵盖各种极限情况和边界情况,而且有时测试用例的数量还会比较大,甚至会重复测试同一个测试用例。因此我们的程序能够通过样例测试,未必能够通过所有的测试用例的测试,一方面要全面考虑所有可能的极限情况和边界情况,一方面程序要有足够的效率。

May 5, 2008

昨天回来之后,一早被安插到5L的YY(运营)团队去学习YY. 向大师引路指点全局,费翼兄给了非常热情的介绍和指导。
也才算对YY有了个新的认识:)

所谓用户 如果变成了客户,中间是两个因素的作用:欲望+能力
大方向来说,吸引更多的0级用户,培育0.5级用户,提升1级用户和N级用户的比例。

怎么去区分用户:RFM模型是非常常用的一个方法 

  根据美国数据库营销研究所Arthur Hughes的研究,客户数据库中有三个神奇的要素,这三个要素构成了数据分析最好的指标:

  • 最近一次消费(Recency)
  • 消费频率(Frenquency)
  • 消费金额(Monetary)

RFM可以用来提高客户的交易次数。业界常用的DM(直接邮寄),常常一次寄发成千上万封邮购清单,其实这是很浪费钱的。根据统计(以一般邮购日用品而言),如果将所有R(Recency)的客户分为五级,最好的第五级回函率是第四级的三倍,因为这些客户刚完成交易不久,所以会更注意同一公司的产品信息。如果用M(Monetary)来把客户分为五级,最好与次好的平均回复率,几乎没有显著差异。

  有些人会用客户绝对贡献金额来分析客户是否流失,但是绝对金额有时会曲解客户行为。因为每个商品价格可能不同,对不同产品的促销有不同的折扣,所以采用相对的分级(例如R、F、M都各分为五级)来比较消费者在级别区间的变动,则更可以显现出相对行为。企业用R、F的变化,可以推测客户消费的异动状况,根据客户流失的可能性,列出客户,再从M(消费金额)的角度来分析,就可以把重点放在贡献度高且流失机会也高的客户上,重点拜访或联系,以最有效的方式挽回更多的商机。 

漏斗模型:
是页面点击流数据仓库的一个重要的应用,类似漏斗过滤一样的,有效用户点击越来越少。
用户在发生试图交易的时候,页面流程越长 流失率越大。
如果某个页面的流失率超过一个伐值,可以判定这个页面的UE有严重的BUG需要修复。

运维的最重要的KPI是活跃会员总数,所以监控其波动 和分析其变动原因是YY团队很重要的一个保障工作,但是核心的工作是提升KPI。引导客户使用新的应用场景是提升用户参与度的一个
持续活跃率:指老用户在最近的一个统计周期内又活跃的比例,反应了用户粘滞度的大小。对老手最好的策略就是不用除了人文关怀之外的任何策略。

DM要做的事情就是把YY的事情量化,DW要做的事情就是把量化的事情用合适合理的形式展现给内部用户。

Tags: ,,.
May 2, 2008

今天在南京的地铁上到珠江路的时候报站非常的映像深刻:
珠江路糖果站
这让我非常的纳闷,难道周围又开了新的糖果中心?朋友的解释让我非常的开心。

这可不仅仅是一个故事。 说 
有一天 一个妈妈带着小孩坐地铁
小孩哇哇的就哭了,闹着不肯下车,年轻的妈妈也比较崩溃的时候 一个珠江路地铁的工作人员(估计是个MM)塞给小孩嘴里一颗糖,然后小孩就安静了。从此之后,珠江路的工作人员口袋里总塞着一个糖果。

站台也被改成了 珠江路糖果站。

------------------------

我非常的喜欢这个故事!
非常!

考证后:
这样的

据珠江路站站长葛蔚介绍,糖果车站缘起一个真实的感人故事:一对夫妻带着患病的孩子到南京儿童医院治病后,乘地铁返回。在珠江路地铁站,患病的孩子哭着说想吃糖,囊中羞涩的父母很为难。一名地铁员工看到这一幕,从口袋中掏出为自己儿子准备的糖果,送到患病孩子的手中,并嘱咐孩子要听父母的话,早日健康。从那以后,珠江路地铁站的所有员工,都带着糖果上班。孩子们都亲切地把珠江路站叫做糖果车站