CF 286B(Shifting-deque)

B. Shifting
time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

John Doe has found the beautiful permutation formula.

Let's take permutation p = p1, p2, ..., pn.
Let's define transformation f of this permutation:

 k (k > 1) 是每段长度, r 是最大满足 rk ≤ 的整数
 把 r
段和尾剩余部分(如果有)左移.

求序列 f(f( ... f(p = [1, 2, ..., n], 2) ... , n - 1), n) .

Input

一行 n (2 ≤ n ≤ 106).

Output

Print n distinct space-separated integers from 1 to n —
a beautiful permutation of size n.

Sample test(s)
input
2
output
2 1
input
3
output
1 3 2
input
4
output
4 2 3 1
Note

A note to the third test sample:

  • f([1, 2, 3, 4], 2) = [2, 1, 4, 3]
  • f([2, 1, 4, 3], 3) = [1, 4, 2, 3]
  • f([1, 4, 2, 3], 4) = [4, 2, 3, 1]
这题我是用WJMZBMR的神模拟过的。
先普及一下deque< >
deque<int> deq;   //建立双端队列
deq.push_back(x) 
deq.push_front(x) 
deq.pop_back(x) 
deq.pop_back(x) 
然后可以模拟了,每次把每段的最后搬上来。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
#define MAXN (1000000+10)
deque<int> deq;
int n;
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++) deq.push_back(i);
	for (int i=2;i<=n;i++)
	{
		int l=(n-1)/i*i;deq.push_back(deq[l]);
		while (l-i>=0)
		{
			deq[l]=deq[l-i];
			l-=i;
		}
		deq.pop_front();
	}
	for (int i=0;i<deq.size();i++) cout<<deq[i]<<' ';
	return 0;
}

UVA 10905(Children's Game-C的qsort函数和sprintf)

4th IIUC Inter-University Programming
Contest, 2005

A

Children’s Game

Input: standard input
Output: standard output

Problemsetter: Md. Kamruzzaman

给你 N 个正数. 如 123, 124, 56, 90 ,它们可以拼成 1231245690, 1241235690, 5612312490, 9012312456, 9056124123 etc. 有 24 种拼法, 9056124123 最大.

请你找出这个最大的数。

Input

每组数据第一行为 N (≤ 50). 接下来1行有 N 个正数,数据以  0 结尾.

Output

对每组数据,输出最大的拼法.

Sample Input

Output for Sample Input

4
123 124 56 90
5
123 124 56 90 9
5
9 9 9 9 9
0

9056124123
99056124123
99999


按照字符串连起来后(len相等)的字符串比较

值得一提的是qsort中cmp的写法(char[]无法用sort排序)

以及sprintf(占位符随便改,可以将任意类型转换成字符串)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<functional>
using namespace std;
#define MAXN (100+10)
int cmp(const void *a1,const void *b1)
{
	char *a=(char*)a1,*b=(char*)b1;
	char _a[MAXN*2]={0},_b[MAXN*2]={0};
	sprintf(_a,"%s%s",a,b);
	sprintf(_b,"%s%s",b,a);
	return strcmp(_b,_a);
}
int n;
char a[MAXN][MAXN];
int main()
{
	while(scanf("%d",&n)&&n)
	{
		for (int i=1;i<=n;i++) scanf("%s",a[i]);
		qsort(a+1,n,sizeof(a[1]),cmp);
		for (int i=1;i<=n;i++) printf("%s",a[i]);
		printf("n");
	}
	return 0;
}

STL list链表的用法详解

转载自:http://www.cnblogs.com/this-543273659/archive/2011/08/01/2123373.html

本文以List容器为例子,介绍了STL的基本内容,从容器到迭代器,再到普通函数,而且例子丰富,通俗易懂。不失为STL的入门文章,新手不容错过!

 

  0 前言

  1 定义一个list

  2 使用list的成员函数push_back和push_front插入一个元素到list中

  3 list的成员函数empty()

  4 用for循环来处理list中的元素

  5 用STL的通用算法for_each来处理list中的元素

  6 用STL的通用算法count_if()来统计list中的元素个数

  7 使用count_if()的一个更加复杂的函数对象。

  8 使用STL通用算法find()在list中查找对象

  9 使用STL通用算法find_if()在list中搜索对象

  10 使用STL通用算法search在list中找一个序列

  11 使用list的成员函数sort()排序一个list。

  12 用list的成员函数插入元素到list中

  13 List 构造函数

  14 使用list成员函数从list中删除元素

  15 用list成员函数remove()从list中删除元素。

  16 使用STL通用算法remove()从list中删除元素

  17 使用STL通用算法stable_partition()和list成员函数splice()来划分一个list

  18 结论

  在field中使用STL

  19 参考书目

 

 

0 前言

 

    这篇文章是关于C++语言的一个新的扩展——标准模板库的(Standard Template Library),也叫STL。

 

    当我第一次打算写一篇关于STL的文章的时候,我不得不承认我当时低估了这个话题的深度和广度。有很多内容要含盖,也有很多详细描述STL的书。因此我重新考虑了一下我原来的想法。我为什么要写这篇文章,又为什么要投稿呢?这会有什麽用呢?有再来一篇关于STL的文章的必要吗?

    当我翻开Musser and Saini的页时,我看到了编程时代在我面前消融。我能看到深夜消失了,目标软件工程出现了。我看到了可维护的代码。一年过去了,我使用STL写的软件仍然很容易维护。让人吃惊的是其他人可以没有我而维护的很好!

    然而,我也记得在一开始的时候很难弄懂那些技术术语。一次,我买了Musser&Saini,每件事都依次出现,但是在那以前我最渴望得到的东西是一些好的例子。

    当我开始的时候,作为C++一部分的Stroustrup还没出来,它覆盖了STL。

    因此我想写一篇关于一个STL程序员的真实生活的文章可能会有用。如果我手上有一些好的例子的话,特别是象这样的新题目,我会学的更快。

    另外一件事是STL应该很好用。因此,理论上说,我们应该可以马上开始使用STL。

    什麽是STL呢?STL就是Standard Template Library,标准模板库。这可能是一个历史上最令人兴奋的工具的最无聊的术语。从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合。这里的“容器”和算法的集合指的是世界上很多聪明人很多年的杰作。

    STL的目的是标准化组件,这样你就不用重新开发它们了。你可以仅仅使用这些现成的组件。STL现在是C++的一部分,因此不用额外安装什麽。它被内建在你的编译器之内。因为STL的list是一个简单的容器,所以我打算从它开始介绍STL如何使用。如果你懂得了这个概念,其他的就都没有问题了。另外,list容器是相当简单的,我们会看到这一点。

    这篇文章中我们将会看到如何定义和初始化一个list,计算它的元素的数量,从一个list里查找元素,删除元素,和一些其他的操作。要作到这些,我们将会讨论两个不同的算法,STL通用算法都是可以操作不止一个容器的,而list的成员函数是list容器专有的操作。

    这是三类主要的STL组件的简明纲要。STL容器可以保存对象,内建对象和类对象。它们会安全的保存对象,并定义我们能够操作的这个对象的接口。放在蛋架上的鸡蛋不会滚到桌上。它们很安全。因此,在STL容器中的对象也很安全。我知道这个比喻听起来很老土,但是它很正确。

    STL算法是标准算法,我们可以把它们应用在那些容器中的对象上。这些算法都有很著名的执行特性。它们可以给对象排序,删除它们,给它们记数,比较,找出特殊的对象,把它们合并到另一个容器中,以及执行其他有用的操作。

    STL iterator就象是容器中指向对象的指针。STL的算法使用iterator在容器上进行操作。Iterator设置算法的边界,容器的长度,和其他一些事情。举个例子,有些iterator仅让算法读元素,有一些让算法写元素,有一些则两者都行。 Iterator也决定在容器中处理的方向。

    你可以通过调用容器的成员函数begin()来得到一个指向一个容器起始位置的iterator。你可以调用一个容器的 end() 函数来得到过去的最后一个值(就是处理停在那的那个值)。

    这就是STL所有的东西,容器、算法、和允许算法工作在容器中的元素上的iterator。算法以合适、标准的方法操作对象,并可通过iterator得到容器精确的长度。一旦做了这些,它们就在也不会“跑出边界”。还有一些其他的对这些核心组件类型有功能性增强的组件,例如函数对象。我们将会看到有关这些的例子,现在 ,我们先来看一看STL的list。

 

 

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

 

1 定义一个list

 

我们可以象这样来定义一个STL的list:

#include <string>

#include <list>

int main (void)

{

list<string> Milkshakes;

return 0;

}

这就行了,你已经定义了一个list。简单吗?list<string> Milkshakes这句是你声明了list<string>模板类的一个实例,然后就是实例化这个类的一个对象。但是我们别急着做这个。在这一步其实你只需要知道你定义了 一个字符串的list。你需要包含提供STL list类的头文件。我用gcc 2.7.2在我的Linux上编译这个测试程序,例如:

 

g++ test1.cpp -o test1

 

注意iostream.h这个头文件已经被STL的头文件放弃了。这就是为什么这个例子中没有它的原因。

 

现在我们有了一个list,我们可以看实使用它来装东西了。我们将把一个字符串加到这个list里。有一个非常 重要的东西叫做list的值类型。值类型就是list中的对象的类型。在这个例子中,这个list的值类型就是字符串,string , 这是因为这个list用来放字符串。

 

 

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

 

2 使用list的成员函数push_back和push_front插入一个元素到list中

 

#include <string>

#include <list>

 

int main (void)

{

list<string> Milkshakes;

Milkshakes.push_back("Chocolate");

Milkshakes.push_back("Strawberry");

Milkshakes.push_front("Lime");

Milkshakes.push_front("Vanilla");

return 0;

}

我们现在有个4个字符串在list中。list的成员函数push_back()把一个对象放到一个list的后面,而 push_front()把对象放到前面。我通常把一些错误信息push_back()到一个list中去,然后push_front()一个标题到list中, 这样它就会在这个错误消息以前打印它了。

 

 

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

 

3 list的成员函数empty()

 

知道一个list是否为空很重要。如果list为空,empty()这个成员函数返回真。 我通常会这样使用它。通篇程序我都用push_back()来把错误消息放到list中去。然后,通过调用empty() 我就可以说出这个程序是否报告了错误。如果我定义了一个list来放信息,一个放警告,一个放严重错误, 我就可以通过使用empty()轻易的说出到底有那种类型的错误发生了。

我可以整理这些list,然后在打印它们之前,用标题来整理它们,或者把它们排序成类。

 

/*

|| Using a list to track and report program messages and status

*/

#include <iostream.h>

#include <string>

#include <list>

int main (void)

{

    #define OK 0

    #define INFO 1

    #define WARNING 2

    int return_code;

    list<string> InfoMessages;

    list<string> WarningMessages;

 

    // during a program these messages are loaded at various points

    InfoMessages.push_back("Info: Program started");

    // do work...

    WarningMessages.push_back("Warning: No Customer records have been found");

    // do work...

 

  return_code = OK;

 

    if  (!InfoMessages.empty()) {          // there were info messages

       InfoMessages.push_front("Informational Messages:");

       // ... print the info messages list, we'll see how later

       return_code = INFO;

    }

 

    if  (!WarningMessages.empty()) {       // there were warning messages

       WarningMessages.push_front("Warning Messages:");

       // ... print the warning messages list, we'll see how later

       return_code = WARNING;

    }

 

    // If there were no messages say so.

    if (InfoMessages.empty() && WarningMessages.empty()) {

       cout << "There were no messages " << endl;

    }

 

    return return_code;

}

 

 

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

 

4 用for循环来处理list中的元素

 

我们想要遍历一个list,比如打印一个中的所有对象来看看list上不同操作的结果。要一个元素一个元素的遍历一个list, 我们可以这样做:

 

/*

|| How to print the contents of a simple STL list. Whew!

*/

#include <iostream.h>

#include <string>

#include <list>

 

int main (void)

{

    list<string> Milkshakes;

    list<string>::iterator MilkshakeIterator;

 

    Milkshakes.push_back("Chocolate");

    Milkshakes.push_back("Strawberry");

    Milkshakes.push_front("Lime");

    Milkshakes.push_front("Vanilla");

 

    // print the milkshakes

    Milkshakes.push_front("The Milkshake Menu");

    Milkshakes.push_back("*** Thats the end ***");

    for (MilkshakeIterator=Milkshakes.begin();

           MilkshakeIterator!=Milkshakes.end();

            ++MilkshakeIterator)

    {

      // dereference the iterator to get the element

      cout << *MilkshakeIterator << endl;

    }

}

这个程序定义了一个iterator,MilkshakeIterator。我们把它指向了这个list的第一个元素。 这可以调用Milkshakes.begin()来作到,它会返回一个指向list开头的iterator。然后我们把它和Milkshakes.end()的 返回值来做比较,当我们到了那儿的时候就停下来。

 

容器的end()函数会返回一个指向容器的最后一个位置的iterator。当我们到了那里,就停止操作。 我们不能不理容器的end()函数的返回值。我们仅知道它意味着已经处理到了这个容器的末尾,应该停止处理了。 所有的STL容器都要这样做。

 

在上面的例子中,每一次执行for循环,我们就重复引用iterator来得到我们打印的字符串。

 

在STL编程中,我们在每个算法中都使用一个或多个iterator。我们使用它们来存取容器中的对象。 要存取一个给定的对象,我们把一个iterator指向它,然后间接引用这个iterator。

 

这个list容器,就象你所想的,它不支持在iterator加一个数来指向隔一个的对象。 就是说,我们不能用Milkshakes.begin()+2来指向list中的第三个对象,因为STL的list是以双链的list来实现的, 它不支持随机存取。vector和deque(向量和双端队列)和一些其他的STL的容器可以支持随机存取。

 

上面的程序打印出了list中的内容。任何人读了它都能马上明白它是怎麽工作的。它使用标准的iterator和标准 的list容器。没有多少程序员依赖它里面装的东西, 仅仅是标准的C++。这是一个向前的重要步骤。这个例子使用STL使我们的软件更加标准。

 

 

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

 

5 用STL的通用算法for_each来处理list中的元素

 

使用STL list和 iterator,我们要初始化、比较和给iterator增量来遍历这个容器。STL通用的for_each 算法能够减轻我们的工作。

/*

|| How to print a simple STL list MkII

*/

#include <iostream.h>

#include <string>

#include <list>

#include <algorithm>

 

PrintIt (string& StringToPrint)

{

    cout << StringToPrint << endl;

}

 

int main (void)

{

    list<string> FruitAndVegetables;

    FruitAndVegetables.push_back("carrot");

    FruitAndVegetables.push_back("pumpkin");

    FruitAndVegetables.push_back("potato");

    FruitAndVegetables.push_front("apple");

    FruitAndVegetables.push_front("pineapple");

 

    for_each  (FruitAndVegetables.begin(), FruitAndVegetables.end(), PrintIt);

}

在这个程序中我们使用STL的通用算法for_each()来遍历一个iterator的范围,然后调用PrintIt()来处理每个对象。 我们不需要初始化、比较和给iterator增量。for_each()为我们漂亮的完成了这些工作。我们执行于对象上的 操作被很好的打包在这个函数以外了,我们不用再做那样的循环了,我们的代码更加清晰了。

for_each算法引用了iterator范围的概念,这是一个由起始iterator和一个末尾iterator指出的范围。 起始iterator指出操作由哪里开始,末尾iterator指明到哪结束,但是它不包括在这个范围内。用STL的通用算法count()来统计list中的元素个数。

 

 

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

 

5.2用STL的通用算法count()来统计list中的元素个数

 

STL的通用算法count()和count_if()用来给容器中的对象记数。就象for_each()一样,count()和count_if() 算法也是在iterator范围内来做的。

 

让我们在一个学生测验成绩的list中来数一数满分的个数。这是一个整型的List。

 

/*

|| How to count objects in an STL list

*/

#include <list>

#include <algorithm>

int main (void)

{

    list<int> Scores;

    Scores.push_back(100);

    Scores.push_back(80);

    Scores.push_back(45);

    Scores.push_back(75);

    Scores.push_back(99);

    Scores.push_back(100);

 

    int NumberOf100Scores(0);

    //count (Scores.begin(), Scores.end(), 100, NumberOf100Scores);

    NumberOf100Scores = count(Scores.begin(), Scores.end(), 100);

    cout << "There were " << NumberOf100Scores << " scores of 100" << endl;

}

 

count()算法统计等于某个值的对象的个数。上面的例子它检查list中的每个整型对象是不是100。每次容器中的对象等于100,它就给NumberOf100Scores加1。这是程序的输出:

程序的输出:

There were 2 scores of 100

 

 

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

 

6.用STL的通用算法count_if()来统计list中的元素个数

 

    count_if()是count()的一个更有趣的版本。他采用了STL的一个新组件,函数对象。count_if() 带一个函数对象的参数。函数对象是一个至少带有一个operator()方法的类。有些STL算法作为参数接收函数对象并调用这个函数对象的operator()方法。

    函数对象被约定为STL算法调用operator时返回true或false。它们根据这个来判定这个函数。举个例子会说的更清楚些。

    count_if()通过传递一个函数对象来作出比count()更加复杂的评估以确定一个对象是否应该被记数。

 

    在这个例子里我们将数一数牙刷的销售数量。我们将提交包含四个字符的销售码和产品说明的销售记录。

 

/* || Using a function object to help count things */

#include <string>

#include <list>

#include <algorithm>

 

const string ToothbrushCode("0003");

 

class IsAToothbrush

{

public:

    bool operator() ( string& SalesRecord )

    {

        return SalesRecord.substr(0,4)==ToothbrushCode;

    }

};

 

int main (void)

{

    list<string> SalesRecords;

    SalesRecords.push_back("0001 Soap");

    SalesRecords.push_back("0002 Shampoo");

    SalesRecords.push_back("0003 Toothbrush");

    SalesRecords.push_back("0004 Toothpaste");

    SalesRecords.push_back("0003 Toothbrush");

    int NumberOfToothbrushes(0);

    count_if (SalesRecords.begin(), SalesRecords.end(), IsAToothbrush(), NumberOfToothbrushes);

    cout << "There were " << NumberOfToothbrushes << " toothbrushes sold" << endl;

}

 

这是这个程序的输出:

There were 2 toothbrushes sold

 

这个程序是这样工作的:定义一个函数对象类IsAToothbrush,这个类的对象能判断出卖出的是否是牙刷 。如果这个记录是卖出牙刷的记录的话,函数调用operator()返回一个true,否则返回false。

 

count_if()算法由第一和第二两个iterator参数指出的范围来处理容器对象。它将对每个 IsAToothbrush()返回true的容器中的对象增加NumberOfToothbrushes的值。

 

最后的结果是NumberOfToothbrushes这个变量保存了产品代码域为"0003"的记录的个数,也就是牙刷的个数。

 

注意count_if()的第三个参数IsAToothbrush(),它是由它的构造函数临时构造的一个对象。你可以把IsAToothbrush类的一个临时对象 传递给count_if()函数。count_if()将对该容器的每个对象调用这个函数。

 

 

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

 

7 使用count_if()的一个更加复杂的函数对象。

 

使用count_if()的一个更加复杂的函数对象。

    我们可以更进一步的研究一下函数对象。假设我们需要传递更多的信息给一个函数对象。我们不能通过调用operator来作到这点,因为必须定义为一个list的中的对象的类型。 然而我们通过为IsAToothbrush指出一个非缺省的构造函数就可以用任何我们所需要的信息来初始化它了。 例如,我们可能需要每个牙刷有一个不定的代码。我们可以把这个信息加到下面的函数对象中:

 

/*

|| Using a more complex function object

*/

#include <iostream.h>

#include <string>

#include <list>

#include <algorithm>

 

class IsAToothbrush

{

public:

    IsAToothbrush(string& InToothbrushCode) : ToothbrushCode(InToothbrushCode) {}

    bool operator() (string& SalesRecord)

    {

        return SalesRecord.substr(0,4)==ToothbrushCode;

    }

private:

    string ToothbrushCode;

};

 

int main (void)

{

    list<string> SalesRecords;

 

    SalesRecords.push_back("0001 Soap");

    SalesRecords.push_back("0002 Shampoo");

    SalesRecords.push_back("0003 Toothbrush");

    SalesRecords.push_back("0004 Toothpaste");

    SalesRecords.push_back("0003 Toothbrush");

 

    string VariableToothbrushCode("0003");

 

    int NumberOfToothbrushes(0);

    count_if (SalesRecords.begin(), SalesRecords.end(), IsAToothbrush(VariableToothbrushCode), NumberOfToothbrushes);

    cout << "There were  "

         << NumberOfToothbrushes

         << " toothbrushes matching code "

         << VariableToothbrushCode

         << " sold"

         << endl;

}

 

程序的输出是:

There were 2 toothbrushes matching code 0003 sold

 

这个例子演示了如何向函数对象传递信息。你可以定义任意你想要的构造函数,你可以再函数对象中做任何你 想做的处理,都可以合法编译通过。

 

你可以看到函数对象真的扩展了基本记数算法。

 

到现在为止,我们都学习了:

 

定义一个list

向list中加入元素

如何知道list是否为空

如何使用for循环来遍历一个list

如何使用STL的通用算法for_each来遍历list

list成员函数begin() 和 end() 以及它们的意义

iterator范围的概念和一个范围的最后一个位置实际上并不被处理这一事实

如何使用STL通用算法count()和count_if()来对一个list中的对象记数

如何定义一个函数对象

我选用这些例子来演示list的一般操作。如果你懂了这些基本原理,你就可以毫无疑问的使用STL了 建议你作一些练习。我们现在用一些更加复杂的操作来扩展我们的知识,包括list成员函数和STL通用算法。

 

输出是:

Pineapple

 

如果没有找到指出的对象,就会返回Fruit.end()的值,要是找到了就返回一个指着找到的对象的iterator

 

 

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

 

8.使用STL通用算法find()在list中查找对象

 

我们如何在list中查找东西呢?

STL的通用算法find()和find_if()可以做这些。

就象for_each(), count(), count_if() 一样,这些算法也使用iterator范围,这个范围指出一个list或任意其他容器中的一部分来处理。通常首iterator指着开始的位置,次iterator指着停止处理的地方。由次iterator指出的元素不被处理。

这是find()如何工作:

/*

|| How to find things in an STL list

*/

#include <string>

#include <list>

#include <algorithm>

 

int main (void)

{

    list<string> Fruit;

    list<string>::iterator FruitIterator;

 

    Fruit.push_back("Apple");

    Fruit.push_back("Pineapple");

    Fruit.push_back("Star Apple");

 

    FruitIterator = find (Fruit.begin(), Fruit.end(), "Pineapple");

 

    if (FruitIterator == Fruit.end())

    {

        cout << "Fruit not found in list" << endl;

    }

    else

    {

        cout << *FruitIterator << endl;

    }

}

 

输出是:

Pineapple

如果没有找到指出的对象,就会返回Fruit.end()的值,要是找到了就返回一个指着找到的对象i

 

 

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

 

9.使用STL通用算法find_if()在list中搜索对象

 

    这是find()的一个更强大的版本。这个例子演示了find_if(),它接收一个函数对象的参数作为参数,并使用它来做更复杂的评价对象是否和给出的查找条件相付。

    假设我们的list中有一些按年代排列的包含了事件和日期的记录。我们希望找出发生在1997年的事件。

 

这是find()的一个更强大的版本。这个例子演示了find_if(),它接收一个函数对象的参数作为参数, 并使用它来做更复杂的评价对象是否和给出的查找条件相付。假设我们的list中有一些按年代排列的包含了事件和日期的记录。我们希望找出发生在1997年的事件。

/*

|| How to find things in an STL list MkII

*/

#include <string>

#include <list>

#include <algorithm>

 

class EventIsIn1997

{

public:

    bool operator () (string& EventRecord)

    {

        // year field is at position 12 for 4 characters in EventRecord

        return EventRecord.substr(12,4)=="1997";

    }

};

 

int main (void)

{

    list<string> Events;

 

    // string positions 0123456789012345678901234567890123456789012345

    Events.push_back("07 January 1995 Draft plan of house prepared");

    Events.push_back("07 February 1996 Detailed plan of house prepared");

    Events.push_back("10 January 1997 Client agrees to job");

    Events.push_back("15 January 1997 Builder starts work on bedroom");

    Events.push_back("30 April 1997 Builder finishes work");

 

    list<string>::iterator EventIterator = find_if (Events.begin(), Events.end(), EventIsIn1997());

 

    // find_if completes the first time EventIsIn1997()() returns true

    // for any object. It returns an iterator to that object which we

    // can dereference to get the object, or if EventIsIn1997()() never

    // returned true, find_if returns end()

    if (EventIterator==Events.end())

    {

        cout << "Event not found in list" << endl;

    }

    else

    {

        cout << *EventIterator << endl;

    }

}

这是程序的输出:

10 January 1997 Client agrees to job

 

 

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

 

10.使用STL通用算法search在list中找一个序列

 

一些字符在STL容器中很好处理,让我们看一看一个难处理的字符序列。我们将定义一个list来放字符。

list<char> Characters;

 

现在我们有了一个字符序列,它不用任何帮助就知道然后管理内存。它知道它是从哪里开始、到哪里结束。 它非常有用。我不知道我是否说过以null结尾的字符数组。

 

让我们加入一些我们喜欢的字符到这个list中:

 

Characters.push_back('');

Characters.push_back('');

Characters.push_back('1');

Characters.push_back('2');

我们将得到多少个空字符呢?

int NumberOfNullCharacters(0);

count(Characters.begin(), Characters.end(), '', NumberOfNullCharacters);

cout << "We have " << NumberOfNullCharacters << endl;

让我们找字符'1'

list<char>::iterator Iter;

Iter = find(Characters.begin(), Characters.end(), '1');

cout << "We found " << *Iter << endl;

这个例子演示了STL容器允许你以更标准的方法来处理空字符。现在让我们用STL的search算法来搜索容器中 的两个null。就象你猜的一样,STL通用算法search()用来搜索一个容器,但是是搜索一个元素串,不象find()和find_if() 只搜索单个的元素。

/*

|| How to use the search algorithm in an STL list

*/

#include <string>

#include <list>

#include <algorithm>

 

int main ( void)

{

    list<char> TargetCharacters;

    list<char> ListOfCharacters;

 

    TargetCharacters.push_back('');

    TargetCharacters.push_back('');

 

    ListOfCharacters.push_back('1');

    ListOfCharacters.push_back('2');

    ListOfCharacters.push_back('');

    ListOfCharacters.push_back('');

 

    list<char>::iterator PositionOfNulls = search(ListOfCharacters.begin(), ListOfCharacters.end(), TargetCharacters.begin(), TargetCharacters.end());

 

    if (PositionOfNulls!=ListOfCharacters.end())

        cout << "We found the nulls" << endl;

}

The output of the program will be 这是程序的输出:

We found the nulls

 

search算法在一个序列中找另一个序列的第一次出现的位置。在这个例子里我们在ListOfCharacters中 找TargetCharacters这个序列的第一次出现,TargetCharacters是包含两个null字符的序列。 search的参数是两个指着查找目标的iterator和两个指着搜索范围的iterators。 因此我们我们在整个的ListOfCharacters的范围内查找TargetCharacters这个list的整个序列。

 

如果TargetCharacters被发现,search就会返回一个指着ListOfCharacters中序列匹配的第一个 字符的iterator。如果没有找到匹配项,search返回ListOfCharacters.end()的值。

 

 

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

 

11 使用list的成员函数sort()排序一个list。

 

    要排序一个list,我们要用list的成员函数sort(),而不是通用算法sort()。所有我们用过的算法都是通用算法。然而,在STL中有时容器支持它自己对一个特殊算法的实现,这通常是为了提高性能。

    在这个例子中,list容器有它自己的sort算法,这是因为通用算法仅能为那些提供随机存取里面元素的容器排序,而由于list是作为一个连接的链表实现的,它不支持对它里面的元素随机存取。所以就需要一个特殊的 sort()成员函数来排序list。

    由于各种原因,容器在性能需要较高或有特殊效果需求的场合支持外部函数(extra functions), 这通过利用构造函数的结构特性可以作到。

 

/* || How to sort an STL list */

#include <string>

#include <list>

#include <algorithm>

PrintIt (string& StringToPrint)

{

cout << StringToPrint << endl;

}

 

int main (void)

{

    list<string> Staff;

    list<string>::iterator PeopleIterator;

    Staff.push_back("John");

    Staff.push_back("Bill");

    Staff.push_back("Tony");

    Staff.push_back("Fidel");

    Staff.push_back("Nelson");

    cout << "The unsorted list " << endl;

    for_each(Staff.begin(), Staff.end(), PrintIt );

    Staff.sort();

    cout << "The sorted list " << endl;

    for_each(Staff.begin(), Staff.end(), PrintIt);

}

 

输出是:

The unsorted list  John Bill Tony Fidel Nelson The sorted list  Bill Fidel John Nelson Tony

 

 

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

 

12.用list的成员函数插入元素到list中

 

    list的成员函数push_front()和push_back()分别把元素加入到list的前面和后面。你可以使用insert() 把对象插入到list中的任何地方。

    insert()可以加入一个对象,一个对象的若干份拷贝,或者一个范围以内的对象。这里是一些插入对象到list中的例子:

 

/*

|| Using insert to insert elements into a list.

*/

#include <list>

 

int main (void)

{

    list<int> list1;

 

    /*

    || Put integers 0 to 9 in the list

    */

    for (int i = 0; i < 10; ++i) list1.push_back(i);

 

    /*

    || Insert -1 using the insert member function

    || Our list will contain -1,0,1,2,3,4,5,6,7,8,9

    */

    list1.insert(list1.begin(), -1);

 

    /*

    || Insert an element at the end using insert

    || Our list will contain -1,0,1,2,3,4,5,6,7,8,9,10

    */

    list1.insert(list1.end(), 10);

 

    /*

    || Inserting a range from another container

    || Our list will contain -1,0,1,2,3,4,5,6,7,8,9,10,11,12

    */

    int IntArray[2] = {11,12};

    list1.insert(list1.end(), &IntArray[0], &IntArray[2]);

 

    /*

    || As an exercise put the code in here to print the lists!

    || Hint: use PrintIt and accept an interger

    */

}

注意,insert()函数把一个或若干个元素插入到你指出的iterator的位置。你的元素将出现在 iterator指出的位置以前。

 

 

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

 

13.List 构造函数

 

我们已经象这样定义了list:

list<int> Fred;

 

你也可以象这样定义一个list,并同时初始化它的元素:

 

// define a list of 10 elements and initialise them all to 0

list<int> Fred(10, 0);

// list now contains 0,0,0,0,0,0,0,0,0,0

或者你可以定义一个list并用另一个STL容器的一个范围来初始化它,这个STL容器不一定是一个list, 仅仅需要是元素类型相同的的容器就可以。

vector<int> Harry;

Harry.push_back(1);

Harry.push_back(2);

#

// define a list and initialise it with the elements in Harry

list<int> Bill(Harry.begin(), Harry.end());

// Bill now contains 1,2

 

 

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

 

14.使用list成员函数从list中删除元素

 

    list成员函数pop_front()删掉list中的第一个元素,pop_back()删掉最后一个元素。函数erase()删掉由一个iterator指出的元素。还有另一个erase()函数可以删掉一个范围的元素。

 

/* || Erasing objects from a list */

#include <list>

int main (void)

{

    list<int> list1;    // define a list of integers

 

    /* || Put some numbers in the list || It now contains 0,1,2,3,4,5,6,7,8,9 */

    for (int i = 0; i < 10; ++i)

        list1.push_back(i);

 

list1.pop_front();    // erase the first element 0

 

    list1.pop_back();     // erase the last element 9

 

    list1.erase(list1.begin());

    // erase the first element (1) using an iterator

 

    list1.erase(list1.begin(), list1.end());

    // erase all the remaining elements

 

    cout << "list contains " << list1.size() << " elements" << endl;

}

 

输出是:

list contains 0 elements

 

 

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

 

15.用list成员函数remove()从list中删除元素。

 

/* || Using the list member function remove to remove elements */

#include <string>

#include <list>

#include <algorithm>

PrintIt (const string& StringToPrint)

{

cout << StringToPrint << endl;

}

 

int main (void)

{

list<string> Birds;

Birds.push_back("cockatoo");

Birds.push_back("galah");

Birds.push_back("cockatoo");

Birds.push_back("rosella");

Birds.push_back("corella");

cout << "Original list with cockatoos" << endl;

for_each(Birds.begin(), Birds.end(), PrintIt);

Birds.remove("cockatoo");

cout << "Now no cockatoos" << endl;

for_each(Birds.begin(), Birds.end(), PrintIt);

}

 

输出是:

  Original list with cockatoos cockatoo galah cockatoo rosella corella Now no cockatoos galah rosella corella

 

 

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

 

16.使用STL通用算法remove()从list中删除元素

 

通用算法remove()使用和list的成员函数不同的方式工作。一般情况下不改变容器的大小。

/*

|| Using the generic remove algorithm to remove list elements

*/

#include <string>

#include <list>

#include <algorithm>

 

PrintIt(string& AString) { cout << AString << endl; }

 

int main (void)

{

    list<string> Birds;

    list<string>::iterator NewEnd;

 

    Birds.push_back("cockatoo");

    Birds.push_back("galah");

    Birds.push_back("cockatoo");

    Birds.push_back("rosella");

    Birds.push_back("king parrot");

 

    cout << "Original list" << endl;

    for_each(Birds.begin(), Birds.end(), PrintIt);

 

    NewEnd = remove(Birds.begin(), Birds.end(), "cockatoo");

 

    cout << endl << "List according to new past the end iterator" << endl;

    for_each(Birds.begin(), NewEnd, PrintIt);

 

    cout << endl << "Original list now. Care required!" << endl;

    for_each(Birds.begin(), Birds.end(), PrintIt);

}

 

输出结果将为:

Original list

cockatoo

galah

cockatoo

rosella

king parrot

 

List according to new past the end iterator

galah

rosella

king parrot

 

Original list now. Care required!

galah

rosella

king parrot

rosella

king parrot

通用remove()算法返回一个指向新的list的结尾的iterator。从开始到这个新的结尾(不含新结尾元素)的范围 包含了remove后剩下所有元素。你可以用list成员函数erase函数来删除从新结尾到老结尾的部分。

 

 

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

 

17.使用STL通用算法stable_partition()和list成员函数splice()来划分一个list

 

使用STL通用算法stable_partition()和list成员函数splice()来划分一个list

  我们将完成一个稍微有点复杂的例子。它演示STL通用算法stable_partition()算法和一个list成员函数 splice()的变化。注意函数对象的使用和没有使用循环。 通过简单的语句调用STL算法来控制。

stable_partition()是一个有趣的函数。它重新排列元素,使得满足指定条件的元素排在 不满足条件的元素前面。它维持着两组元素的顺序关系。

 

splice 把另一个list中的元素结合到一个list中。它从源list中删除元素。

 

在这个例子中,我们想从命令行接收一些标志和四个文件名。文件名必须’按顺序出现。通过使用stable_partition() 我们可以接收和文件名混为任何位置的标志,并且不打乱文件名的顺序就把它们放到一起。

 

由于记数和查找算法都很易用,我们调用这些算法来决定哪个标志被设置而哪个标志未被设置。 我发现容器用来管理少量的象这样的动态数据。

 

/*

|| Using the STL stable_partition algorithm

|| Takes any number of flags on the command line and

|| four filenames in order.

*/

#include <string>

#include <list>

#include <algorithm>

 

PrintIt ( string& AString { cout << AString << endl; }

 

class IsAFlag {

public:

    bool operator () (string& PossibleFlag) {

        return PossibleFlag.substr(0,1)=="-";

    }

};

 

class IsAFileName {

public:

    bool operator () (string& StringToCheck) {

        return !IsAFlag()(StringToCheck);

    }

};

 

class IsHelpFlag {

public:

    bool operator () (string& PossibleHelpFlag) {

        return PossibleHelpFlag=="-h";

    }

};

 

int main (int argc, char *argv[]) {

 

    list<string> CmdLineParameters; // the command line parameters

    list<string>::iterator StartOfFiles; // start of filenames

    list<string> Flags; // list of flags

    list<string> FileNames; // list of filenames

 

    for (int i = 0; i < argc; ++i)

    CmdLineParameters.push_back(argv[i]);

 

        CmdLineParameters.pop_front(); // we don't want the program name

 

    // make sure we have the four mandatory file names

    int NumberOfFiles(0);

    count_if(CmdLineParameters.begin(), CmdLineParameters.end(), IsAFileName(), NumberOfFiles);

 

    cout << "The " << (NumberOfFiles == 4 ? "correct " : "wrong ") << "number (" << NumberOfFiles << ") of file names were specified" << endl;

 

 // move any flags to the beginning

    StartOfFiles = stable_partition(CmdLineParameters.begin(), CmdLineParameters.end(), IsAFlag());

 

    cout << "Command line parameters after stable partition" << endl;

    for_each(CmdLineParameters.begin(), CmdLineParameters.end(), PrintIt);

 

    // Splice any flags from the original CmdLineParameters list into Flags list.

    Flags.splice(Flags.begin(), CmdLineParameters, CmdLineParameters.begin(), StartOfFiles);

 

    if (!Flags.empty()) {

        cout << "Flags specified were:" << endl;

        for_each(Flags.begin(), Flags.end(), PrintIt);

    }

    else {

        cout << "No flags were specified" << endl;

    }

 

    // parameters list now contains only filenames. Splice them into FileNames list.

    FileNames.splice(FileNames.begin(), CmdLineParameters, CmdLineParameters.begin(), CmdLineParameters.end());

 

    if (!FileNames.empty()) {

        cout << "Files specified (in order) were:" << endl;

        for_each(FileNames.begin(), FileNames.end(), PrintIt);

    }

    else {

        cout << "No files were specified" << endl;

    }

 

    // check if the help flag was specified

    if (find_if(Flags.begin(), Flags.end(), IsHelpFlag())!=Flags.end()) {

        cout << "The help flag was specified" << endl;

    }

 

    // open the files and do whatever you do

 

}

给出这样的命令行:

test17 -w linux -o is -w great

 

输出是:

 

The wrong number (3) of file names were specified

Command line parameters after stable partition

-w

-o

-w

linux

is

great

Flags specified were:

-w

-o

-w

Files specified (in order) were:

linux

is

great

 

 

 

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

 

18 结论

 

  我们仅仅简单的谈了谈你可以用list做的事情。我们没有说明一个对象的用户定义类,虽然这个不难。

 

  如果你懂了刚才说的这些算法背后的概念,那么你使用剩下的那些算法就应该没有问题了。使用STL 最重要的东西就是得到基本理论。

 

  STL的关键实际上是iterator。STL算法作为参数使用iterator,他们指出一个范围,有时是一个范围, 有时是两个。STL容器支持iterator,这就是为什么我们说 list<int>::iterator, 或 list<char>::iterator, 或 list<string>::iterator.

 

  iterator有很好的定义继承性。它们非常有用。某些iterator仅支持对一个容器只读,某些 仅支持写,还有一些仅能向前指,有一些是双向的。有一些iterator支持对一个容器的随机存取。

 

  STL算法需要某个iterator作为“动力” 如果一个容器不提供iterator作为“动力”,那么这个算法将无法编译。例如,list容器仅提供双向的iterator。通常的sort()算法需要随机存取的iterator。这就是为什么我们需要一个特别的list成员函数sort()。

 

  要合适的实际使用STL,你需要仔细学习各种不同的iterator。你需要知道每种容器都支持那类iterator。 你还需要知道算法需要那种iterator,你当然也需要懂得你可以有那种iterator。

 

在field中使用STL

    去年,我曾用STL写过几个商业程序。它在很多方面减少了我的工作量,也排除了很多逻辑错误。

    最大的一个程序有大约5000行。可能最惊人的事情就是它的速度。它读入并处理一个1-2兆的报告文件仅花大约20秒。我是在linux上用gcc2.7.2开发的,现在运行在HP-UX机器上。它一共用了大约50和函数对象和很多容器,这些容器的大小从小list到一个有14,000个元素的map都有。

    一个程序中的函数对象是处于一个继承树中,顶层的函数对象调用低层的函数对象。我大量的使用STL算法for_each() ,find(),find_if(),count()和count_if(),我尽量减少使用程序内部的函数,而使用STL的算法调用。

    STL倾向于自动的把代码组织成清晰的控制和支持模块。通过小心使用函数对象并给它们起有意义的名字,我使它们在我的软件的控制流中流动。

    还有很多关于STL编程要知道的东西,我希望你通过这些例子可以愉快的工作。

    参考数目中的两本书在web上都有勘误表,你可以自己改正它们。

    Stroustrup在每一章后面都有个建议栏,特别是对于出学者有用。正本书比早期的版本更加健谈。它也更大了。书店里还可以找到其他几本关于STL的教科书。去看看,也许你能发现什么。

 

HDU 1234(开门人和关门人-scanf解决带注释数字读入)

开门人和关门人

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 7841    Accepted Submission(s): 4066



Problem Description
每天第一个到机房的人要把门打开,最后一个离开的人要把门关好。现有一堆杂乱的机房签 
到、签离记录,请根据记录找出当天开门和关门的人。 
 


Input
测试输入的第一行给出记录的总天数N ( > 0 )。下面列出了N天的记录。 
每天的记录在第一行给出记录的条目数M ( > 0 ),下面是M行,每行的格式为 

证件号码 签到时间 签离时间 

其中时间按“小时:分钟:秒钟”(各占2位)给出,证件号码是长度不超过15的字符串。

 


Output
对每一天的记录输出1行,即当天开门和关门人的证件号码,中间用1空格分隔。 
注意:在裁判的标准测试输入中,所有记录保证完整,每个人的签到时间在签离时间之前, 
且没有多人同时签到或者签离的情况。 
 


Sample Input
3 1 ME3021112225321 00:00:00 23:59:59 2 EE301218 08:05:35 20:56:35 MA301134 12:35:45 21:40:42 3 CS301111 15:30:28 17:00:10 SC3021234 08:00:00 11:25:25 CS301133 21:45:00 21:58:40
 


Sample Output
ME3021112225321 ME3021112225321 EE301218 MA301134 SC3021234 CS301133
 


Source
 


Recommend
JGShining
 

利用Scanf可以解决时间的读入

%d:%d:%d的占位符

占位符中如果用符号,则表示读取这个符号,但不做任何事

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<functional>
#include<algorithm>
#include<cctype>
#include<iostream>
using namespace std;
int t,n;
struct ID
{
	char s[16];
	int in1,in2,in3,out1,out2,out3;
	int intime(){return in1*3600+in2*60+in3;}
	int outtime(){return out1*3600+out2*60+out3;}
	friend bool operator<(ID a,ID b){return a.intime()<b.intime();}
	friend bool operator>(ID a,ID b){return a.outtime()>b.outtime();}

}p,amin,amax;
int main()
{
	cin>>t;
	while (t--)
	{
		int n;
		cin>>n;
		scanf("%s %d:%d:%d %d:%d:%d",p.s,&p.in1,&p.in2,&p.in3,&p.out1,&p.out2,&p.out3);
		amin=amax=p;
		for (int i=2;i<=n;i++)
		{
			scanf("%s %d:%d:%d %d:%d:%d",p.s,&p.in1,&p.in2,&p.in3,&p.out1,&p.out2,&p.out3);
			if (p<amin) amin=p;
			if (p>amax) amax=p;
		}
		printf("%s %sn",amin.s,amax.s);
	}
	return 0;
}


CF 18A(近似直角三角形判断+向量直角公式+switch+istream&(..&P a))

A. Triangle
time limit per test

2 seconds

memory limit per test

64 megabytes

input

standard input

output

standard output

判断一个格点三角形是直角三角形,近似直角三角形,还是都不是.

Hint:近似直角三角形是指把一个三角形的一个点移动1个单位长度(移动后仍为格点三角形),其能变成直角三角形的非直角三角形

Input

 x1, y1, x2, y2, x3, y3 表示3点坐标(都在格点上),不超过
100.

Output

I直角三角形输出 RIGHT,

近似直角三角形输出
 ALMOST, 都不是输出 NEITHER.

Sample test(s)
input
0 0 2 0 0 1
output
RIGHT
input
2 3 4 5 6 6
output
NEITHER
input
-1 0 2 0 0 1
output
ALMOST

各种判断…

注意格点三角形在移动完可能出现点重合(0向量)

仍满足向量直角公式a X b
= |a||b|

以及switch-case-default的用法。

△:default打错不会提示.

PS:istream& operator<<的重载中 输入的struct 如果不加&,是读不进的。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<functional>
#include<algorithm>
#include<cmath>
using namespace std;
struct P
{
	int x,y;
	P(){}
	P(int _x,int _y):x(_x),y(_y){}
	friend istream& operator>>(istream& cin,P &a){cin>>a.x>>a.y;return cin;	}
	void move(int d)
	{
		if (d==1) x++;
		if (d==-1) x--;
		if (d==2) y++;
		if (d==-2) y--;
		return;
	}
}a[3];
struct V
{
	int x,y;
	V(){}
	V(int _x,int _y):x(_x),y(_y){}
	V(P a,P b):x(b.x-a.x),y(b.y-a.y){}
	friend int operator*(V a,V b){return a.x*b.y-a.y*b.x;}
	int dis2(){return x*x+y*y;	}
	friend bool right_angle(V a,V b){return pow(a*b,2)==a.dis2()*b.dis2();	}
}c[3];
void res_c()
{
	for (int i=0;i<3;i++) c[i]=V(a[i],a[(i+1)%3]);
}
bool is_r_trangle()
{
	res_c();
	for (int i=0;i<3;i++) if (!c[i].dis2()) return 0;
	for (int i=0;i<2;i++)
		for (int j=i+1;j<=2;j++) if (right_angle(c[i],c[j])) {/*cout<<i<<' '<<j<<endl;*/return 1;}
	return 0;
}
int solve()
{
	if (is_r_trangle()) return 1;
	for (int i=0;i<3;i++)
	{
		for (int j=-2;j<=2;j++)
		{
			if (j==0) continue;
			a[i].move(j);
			if (is_r_trangle()){/*cout<<i<<' '<<j<<endl;*/ return 2;}
			a[i].move(-j);
		}
	}
	return 0;
}
int main()
{
	for (int i=0;i<3;i++) cin>>a[i];

	switch (solve())
	{
		case 1:cout<<"RIGHT";break;
		case 2:cout<<"ALMOST";break;
		default:cout<<"NEITHER";
	}
	cout<<endl;

	return 0;
}

extern

基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。

      也就是说extern有两个作用,第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是fun@aBc_int_int#%$也可能是别的,这要看编译器的"脾气"了(不同的编译器采用的方法不一样),为什么这么做呢,因为C++支持函数的重载啊,在这里不去过多的论述这个问题,如果你有兴趣可以去网上搜索,相信你可以得到满意的解释!
    第二,当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块活其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。

2 问题:extern 变量
  在一个源文件里定义了一个数组:char a[6];
  在另外一个文件里用下列语句进行了声明:extern char *a;
  请问,这样可以吗? 
  答案与分析:
  1)、不可以,程序运行时会告诉你非法访问。原因在于,指向类型T的指针并不等价于类型T的数组。extern char *a声明的是一个指针变量而不是字符数组,因此与实际的定义不同,从而造成运行时非法访问。应该将声明改为extern char a[ ]
  2)、例子分析如下,如果a[] = "abcd",则外部变量a=0x61626364 (abcd的ASCII码值),*a显然没有意义
  显然a指向的空间(0x61626364)没有意义,易出现非法内存访问。
  3)、这提示我们,在使用extern时候要严格对应声明时的格式,在实际编程中,这样的错误屡见不鲜。
  4)、extern用在变量声明中常常有这样一个作用,你在*.c文件中声明了一个全局的变量,这个全局的变量如果要被引用,就放在*.h中并用extern来声明

3 问题:当方面修改extern 函数原型
  当函数提供方单方面修改函数原型时,如果使用方不知情继续沿用原来的extern申明,这样编译时编译器不会报错。但是在运行过程中,因为少了或者多了输入参数,往往会照成系统错误,这种情况应该如何解决?
  答案与分析:
  目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供对外部接口的声明,然后调用方include该头文件,从而省去extern这一步。以避免这种错误。
  宝剑有双锋,对extern的应用,不同的场合应该选择不同的做法。

4 问题:extern “C”
  在C++环境下使用C函数的时候,常常会出现编译器无法找到obj模块中的C函数定义,从而导致链接失败的情况,应该如何解决这种情况呢?

  答案与分析:
  C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。
  下面是一个标准的写法:
//在.h文件的头上
#ifdef __cplusplus
#if __cplusplus
extern "C"{
 #endif
 #endif /* __cplusplus */ 
 …
 …
 //.h文件结束的地方
 #ifdef __cplusplus
 #if __cplusplus
}
#endif
#endif /* __cplusplus */ 

5 问题:extern 函数声明
  常常见extern放在函数的前面成为函数声明的一部分,那么,C语言的关键字extern在函数的声明中起什么作用?
  答案与分析:
  如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显的区别:
extern int f(); 和int f();
  当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明函数,在一些复杂的项目中,我比较习惯在所有的函数声明前添加extern修饰。关于这样做的原因和利弊可见下面的这个例子:“用extern修饰的全局变量”

    (1) 在test1.h中有下列声明:
    #ifndef TEST1H
    #define TEST1H
    extern char g_str[]; // 声明全局变量g_str
    void fun1();
    #endif
    (2) 在test1.cpp中
    #include "test1.h"
        char g_str[] = "123456"; // 定义全局变量g_str
        void fun1() { cout << g_str << endl; }
    (3) 以上是test1模块, 它的编译和连接都可以通过,如果我们还有test2模块也想使用g_str,只需要在原文件中引用就可以了
    #include "test1.h"

     void fun2()    { cout << g_str << endl;    }
    以上test1和test2可以同时编译连接通过,如果你感兴趣的话可以用ultraEdit打开test1.obj,你可以在里面找到"123456"这个字符串,但是你却不能在test2.obj里面找到,这是因为g_str是整个工程的全局变量,在内存中只存在一份,test2.obj这个编译单元不需要再有一份了,不然会在连接时报告重复定义这个错误!
    (4) 有些人喜欢把全局变量的声明和定义放在一起,这样可以防止忘记了定义,如把上面test1.h改为
    extern char g_str[] = "123456"; // 这个时候相当于没有extern
    然后把test1.cpp中的g_str的定义去掉,这个时候再编译连接test1和test2两个模块时,会报连接错误,这是因为你把全局变量g_str的定义放在了头文件之后,test1.cpp这个模块包含了test1.h所以定义了一次g_str,而test2.cpp也包含了test1.h所以再一次定义了g_str,这个时候连接器在连接test1和test2时发现两个g_str。如果你非要把g_str的定义放在test1.h中的话,那么就把test2的代码中#include
"test1.h"去掉 
换成:
    extern char g_str[];
    void fun2()   {  cout << g_str << endl;   }
   这个时候编译器就知道g_str是引自于外部的一个编译模块了,不会在本模块中再重复定义一个出来,但是我想说这样做非常糟糕,因为你由于无法在test2.cpp中使用#include "test1.h",那么test1.h中声明的其他函数你也无法使用了,除非也用都用extern修饰,这样的话你光声明的函数就要一大串,而且头文件的作用就是要给外部提供接口使用的,所以 请记住, 只在头文件中做声明,真理总是这么简单

6. extern 和 static

 (1) extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量.
 (2) static 表示静态的变量,分配内存的时候, 存储在静态区,不存储在栈上面.

    static 作用范围是内部连接的关系, 和extern有点相反.它和对象本身是分开存储的,extern也是分开存储的,但是extern可以被其他的对象用extern 引用,而static 不可以,只允许对象本身用它. 具体差别首先,static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变;其次,static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它,如:
    (1) test1.h:
    #ifndef TEST1H
    #define TEST1H
    static char g_str[] = "123456"; 
    void fun1();
    #endif

    (2) test1.cpp:
    #include "test1.h"
    void fun1()  {   cout << g_str << endl;  }
    (3) test2.cpp
    #include "test1.h"
    void fun2()  {   cout << g_str << endl;  }
    以上两个编译单元可以连接成功, 当你打开test1.obj时,你可以在它里面找到字符串"123456",同时你也可以在test2.obj中找到它们,它们之所以可以连接成功而没有报重复定义的错误是因为虽然它们有相同的内容,但是存储的物理地址并不一样,就像是两个不同变量赋了相同的值一样,而这两个变量分别作用于它们各自的编译单元。 也许你比较较真,自己偷偷的跟踪调试上面的代码,结果你发现两个编译单元(test1,test2)的g_str的内存地址相同,于是你下结论static修饰的变量也可以作用于其他模块,但是我要告诉你,那是你的编译器在欺骗你,大多数编译器都对代码都有优化功能,以达到生成的目标程序更节省内存,执行效率更高,当编译器在连接各个编译单元的时候,它会把相同内容的内存只拷贝一份,比如上面的"123456",
位于两个编译单元中的变量都是同样的内容,那么在连接的时候它在内存中就只会存在一份了,如果你把上面的代码改成下面的样子,你马上就可以拆穿编译器的谎言:
    (1) test1.cpp:
    #include "test1.h"
    void fun1()
    {
        g_str[0] = ''a'';
        cout << g_str << endl;
    }

    (2) test2.cpp
    #include "test1.h"
    void fun2()  {  cout << g_str << endl;  }
    (3) void main()     {
        fun1(); // a23456
        fun2(); // 123456
    }
    这个时候你在跟踪代码时,就会发现两个编译单元中的g_str地址并不相同,因为你在一处修改了它,所以编译器被强行的恢复内存的原貌,在内存中存在了两份拷贝给两个模块中的变量使用。正是因为static有以上的特性,所以一般定义static全局变量时,都把它放在原文件中而不是头文件,这样就不会给其他模块造成不必要的信息污染,同样记住这个原则吧!

7. extern 和const

   C++中const修饰的全局常量据有跟static相同的特性,即它们只能作用于本编译模块中,但是const可以与extern连用来声明该常量可以作用于其他编译模块中, 如extern const char g_str[];
    然后在原文件中别忘了定义:     const char g_str[] = "123456"; 

    所以当const单独使用时它就与static相同,而当与extern一起合作的时候,它的特性就跟extern的一样了!所以对const我没有什么可以过多的描述,我只是想提醒你,const char* g_str = "123456" 与 const char g_str[] ="123465"是不同的, 前面那个const 修饰的是char *而不是g_str,它的g_str并不是常量,它被看做是一个定义了的全局变量(可以被其他编译单元使用), 所以如果你像让char*g_str遵守const的全局常量的规则,最好这么定义const
char* const g_str="123456".

auto和static

auto仅在语句块内部使用,初始化可为任何表达式,其特点是当执行流程进入该语句块的时候执行初始化操作,没有默认值。C语言中提供了存储说明符auto,register,extern,static说明的四种存储类别。四种存储类别说明符有两种存储期:自动存储期和静态存储期。其中auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。在函数内部定义的变量成为局部变量。在某些C语言教材中,局部变量称为自动变量,这就与使用可选关键字a
u t o定义局部变量这一作法保持一致。

局部变量仅由其被定义的模块内部的语句所访问。换言之,局部变量在自己的代码模块之外是不可知的。切记:模块以左花括号开始,以右花括号结束。对于局部变量,要了解的最重要的东西是:它们仅存在于被定义的当前执行代码块中,即局部变量在进入模块时生成,在退出模块时消亡。定义局部变量的最常见的代码块是函数。 语言中包括了关键字auto,它可用于定义局部变量。但自从所有的非全局变量的缺省值假定为auto以来,auto就几乎很少使用了。

在C或者以前的C++中,auto关键字基本上可以被无视:比如这个局部变量:int a = 100;auto int a = 100;并没有什么区别。

但是在VC2010中,auto已经有了新的含义,它可以对类型进行推断使得我们在使用的时候可以这样auto a = 100;那么a就是int类型,初始值为100。

Static

静态变量作用范围在一个文件内,程序开始时分配空间,结束时释放空间,默认初始化为0,使用时可以改变其值。

    静态变量或静态函数只有本文件内的代码才能访问它,它的名字在其它文件中不可见。
用法1:函数内部声明的static变量,可作为对象间的一种通信机制
    如果一局部变量被声明为static,那么将只有唯一的一个静态分配的对象,它被用于在该函数的所有调用中表示这个变量。这个对象将只在执行线程第一次到达它的定义使初始化。
用法2:局部静态对象
    对于局部静态对象,构造函数是在控制线程第一次通过该对象的定义时调用。在程序结束时,局部静态对象的析构函数将按照他们被构造的相反顺序逐一调用,没有规定确切时间。
 用法3:静态成员和静态成员函数
  如果一个变量是类的一部分,但却不是该类的各个对象的一部分,它就被成为是一个static静态成员。一个static成员只有唯一的一份副本,而不像常规的非static成员那样在每个对象里各有一份副本。同理,一个需要访问类成员,而不需要针对特定对象去调用的函数,也被称为一个static成员函数。
类的静态成员函数只能访问类的静态成员(变量或函数)。

    进一步详细解释如下:

1.先来介绍它的第一条也是最重要的一条:隐藏

     当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c. 下面是a.c的内容:

char a = 'A'; // global variable

void msg() { printf("Hellon"); }

    下面是main.c的内容:

int main(void) { 

    extern char a; // extern variable must be declared before use

    printf("%c ", a);

    (void)msg();

    return 0;  }

    程序的运行结果是:

A Hello

    你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。

如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。

2. static的第二个作用是保持变量内容的持久

    存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。虽然这种用法不常见,但我还是举一个例子。

#include <stdio.h>

 int fun(void){

static int count = 10; // 事实上此赋值语句从来没有执行过

 return count--;

 }

 int count = 1;

int main(void) {

printf("globalttlocal staticn");

 for(; count <= 10; ++count)

    printf("%dtt%dn", count, fun());

 return 0; }

    程序的运行结果是:

global local static
1  10
2   9
3   8
4   7
5   6
6   5
7   4
8   3
9   2
10  1

3. static的第三个作用是默认初始化为0.其实全局变量也具备这一属性,因为全局变量也存储在静态数据区

注意:

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
int& hash(int x)
{
	static int t=x*2;
	return t;
}
int main()
{
	hash(2);
	hash(3);
	cout<<hash(2)<<hash(3);

	return 0;
}

这段代码中,返回值为4,4.

hash(2) 和  hash(3) 表示同一个函数.

想写hash还是老老实实用Map吧~

    在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是‘’。不妨做个小实验验证一下。

#include <stdio.h>

 int a;

int main(void){

int i;

static char str[10];

printf("integer: %d; string: (begin)%s(end)", a, str);

 return 0;

 }

    程序的运行结果如下integer: 0; string: (begin)(end)

    最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0.

4. 用static声明的函数和变量小结

    static 声明的变量在C语言中有两方面的特征:

1)、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。

2)、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。

 Tips:
A.若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
B.若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
C.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题;
D.如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量(这样的函数被称为:带“内部存储器”功能的的函数)
E.函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。

    函数前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。

扩展分析:

     术语static有着不寻常的历史.起初,在C中引入关键字static是为了表示退出一个块后仍然存在的局部变量。随后,static在C中有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。为了避免引入新的关键字,所以仍使用static关键字来表示这第二种含义。最后,C++重用了这个关键字,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定对象的变量和函数(与Java中此关键字的含义相同)。

全局变量、静态全局变量、静态局部变量和局部变量的区别

变量可以分为:全局变量、静态全局变量、静态局部变量和局部变量。

(1) 按存储区域分,全局变量、静态全局变量和静态局部变量都存放在内存的静态存储区域,局部变量存放在内存的栈区。

(2) 按作用域分,  全局变量在整个工程文件内都有效;静态全局变量只在定义它的文件内有效;静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;局部变量在定义它的函数内有效,但是函数返回后失效。

     全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

    从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。

(1) static 函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件

(2) static全局变量与普通的全局变量有什么区别:static全局变量只初始化一次,防止在其他文件单元中被引用;

(3) static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
(4) static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝.
(5) 全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量的值不可知。

5. C++的static

    C++的static有两种用法:面向过程程序设计的static和面向对象程序设计中的static。前者应用于普通变量和函数,不涉及类;后者主要说明static在类中的作用。

(1)、面向过程设计中的static

1)、静态全局变量

在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量。我们先举一个静态全局变量的例子,如下: 

//Example 1

#include <iostream.h>

void fn();

static int n; //定义静态全局变量

void main()

{

 n=20;

 cout<<n<<endl;

 fn();

}

void fn()

{

 n++;

 cout<<n<<endl;

}

    静态全局变量有以下特点:  

 i )  该变量在全局数据区分配内存;
  ii )  未经初始化的静态全局变量会被程序自动初始化为0(自动变量的值是随机的,除非它被显式初始化);   
  iii ) 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的;

    静态变量都在全局数据区分配内存,包括后面将要提到的静态局部变量。对于一个完整的程序,在内存中的分布情况如下图:

代码区

全局数据区

堆区

栈区

  一般程序的由new产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。细心的读者可能会发现,Example 1中的代码中将 

 static int n; //定义静态全局变量

改为 

 int n; //定义全局变量

    程序照样正常运行。的确,定义全局变量就可以实现变量在文件中的共享,但定义静态全局变量还有以下好处: 

1) 静态全局变量不能被其它文件所用;  

2) 其它文件中可以定义相同名字的变量,不会发生冲突; 

您可以将上述示例代码改为如下:

//Example 2

//File1

#include <iostream.h>

void fn();

static int n; //定义静态全局变量

void main()

{

 n=20;

 cout<<n<<endl;

 fn();

}

//File2

#include <iostream.h>

extern int n;

void fn()

{

 n++;

 cout<<n<<endl;

}

    编译并运行Example 2,您就会发现上述代码可以分别通过编译,但运行时出现错误。试着将  

static int n; //定义静态全局变量

改为  

int n; //定义全局变量

    再次编译运行程序,细心体会全局变量和静态全局变量的区别。  

(2)、静态局部变量

     在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。 我们先举一个静态局部变量的例子,如下: 

//Example 3

#include <iostream.h>

void fn();

void main()

{

 fn();

 fn();

 fn();

}

void fn()

{

 static n=10;

 cout<<n<<endl;

 n++;

}

  通常,在函数体内定义了一个变量,每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体,系统就会收回栈内存,局部变量也相应失效。但是有时候我们需要在两次调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。  静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下



C++ pair

Pair类型概述

pair是一种模板类型,其中包含两个数据值,两个数据的类型可以不同,基本的定义如下:

 

pair<int, string> a;

表示a中有两个类型,第一个元素是int型的,第二个元素是string类型的,如果创建pair的时候没有对其进行初始化,则调用默认构造函数对其初始化。

 

pair<string, string> a("James", "Joy");

也可以像上面一样在定义的时候直接对其初始化。

 

由于pair类型的使用比较繁琐,因为如果要定义多个形同的pair类型的时候,可以时候typedef简化声明:

typedef pair<string, string> author;

author pro("May", "Lily");

author joye("James", "Joyce");

 

 

Pair对象的操作

 

  • 于pair类,由于它只有两个元素,分别名为first和second,因此直接使用普通的点操作符即可访问其成员

pair<string,
string> a("Lily", "Poly"); 

string
name;

name
= pair.second;

  • 生成新的pair对象

可以使用make_pair对已存在的两个数据构造一个新的pair类型:

int a = 8;

string m = "James";

pair<int, string> newone;

newone = make_pair(a, m);

 

 

POJ 3298(用unique离散化优化zkw线段树)

Language:
Antimonotonicity
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 2753   Accepted: 1175

Description

Mary数列是指在一个长度为n的序列(元素大小不超过n)中找到如下的子序列:

Mary0 > Mary1 < Mary2 > Mary3 < ...

请求出它的最长序列大小。

Input

第一行为数据数 T ≤ 50

接下来T行,每行第一个数n( 30000)表示原序列大小,接下来n个数为给定序列

Output

对每组数据输出一行为Mary数列最长长度。

Sample Input

4
5 1 2 3 4 5
5 5 4 3 2 1
5 5 1 4 2 3
5 2 4 1 3 5

Sample Output

1
2
5
3

Source

这题和LIS有异曲同工之妙,都是在给定区间找最大值

显然可以建立两棵线段树(并互相传递值),表示(...?<a[i]) 中使得“..”长度最长的大小,

由于用Unique(指针头,指针尾+1)离散了序列,用-INF和INF表示边界(特别注意离散Hash-map<int,int> Hpos一定要开在Struct外,否则反复建会超时(平衡树用来干这个……)

于是t.t[i][j] 表示第ith线段树的端点值。

i=0表示(1,3,5... 即除1外前面跟了<号的)的数

i=1表示(2,4,6... 即前面跟>的)数


于是本题转化为维护(..?<)的最长长度。

同理建立(..?>),注意特判序列开头那个数(第二个序列的长度必须超过1,表示其并非开头)


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
#define MAXN (30000+10)
#define INF (2139062143)
#define NDEBUG
map<int,int> hpos;
struct SegMentTree  //t[0]->'?<' t[1]->'?>'
{
	int n,M,t[2][MAXN*5],a[MAXN],a_sort[MAXN],size;
	SegMentTree(){}
	SegMentTree(int _n):n(_n)
	{
		M=1;while (M-2<n) M<<=1;
		memset(t,0,sizeof(t));
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		memcpy(a_sort,a,sizeof(a));
		sort(a_sort+1,a_sort+n+1);
		#ifndef NDEBUG
		for (int i=1;i<=n;i++) cout<<a_sort[i]<<' ';
		cout<<endl;
		#endif
		size=unique(a_sort+1,a_sort+n+1)-(a_sort+1);
		#ifndef NDEBUG
		for (int i=1;i<=size;i++) cout<<a_sort[i]<<' ';
		cout<<endl;
		cout<<size;
		#endif
		for (int i=1;i<=size;i++) hpos[a_sort[i]]=i;
		hpos[-INF]=0;hpos[INF]=size+1;
	}
	void update(int x,int type)
	{
		for (x>>=1;x;x>>=1) t[type][x]=max(t[type][x<<1],t[type][(x<<1)^1]);
	}
	void insert(int x,int c,int type)
	{
		x=hpos[x]+M;
		if (t[type][x]<c)
		{
			t[type][x]=c;
			update(x,type);
		}
	}
	int find(int l,int r,int type)
	{
		l=hpos[l];r=hpos[r];
//		if (type) l++; else r--;
		#ifndef NDEBUG
		cout<<l<<' '<<r<<';'<<endl;
		#endif

		l+=M;r+=M;
		int ans=0;
		if (l>=r-1) return 0;
		for (;l^r^1;l>>=1,r>>=1)
		{
			if (~l&1) ans=max(ans,t[type][l+1]);
			if (r&1) ans=max(ans,t[type][r-1]);
		}
		return ans;
	}

}t;
int tt,n,ans;
int main()
{
	#ifndef NDEBUG
	freopen("poj3298.in","r",stdin);
	#endif
	scanf("%d",&tt);
	while (tt--)
	{
		ans=0;
		scanf("%d",&n);
		t=SegMentTree(n);
		for (int i=1;i<=n;i++)
		{
			int value=t.a[i];
			int value1=t.find(-INF,value,0)+1;
			int value2=t.find(value,INF,1)+1;
			t.insert(value,value1,1);
			ans=max(ans,value1);
			#ifndef NDEBUG
			cout<<"Add?> "<<value<<" f[i]="<<value1<<endl;
			#endif
			if (value2==1) continue;
			t.insert(value,value2,0);
			ans=max(ans,value2);
			#ifndef NDEBUG
			cout<<"Add?< "<<value<<" f[i]="<<value2<<endl;
			#endif
		}
		cout<<ans<<endl;
		#ifndef NDEBUG
		for (int i=1;i<=t.M*2;i++) if (t.t[0][i]) cout<<i<<':'<<t.t[0][i]<<' ';
		cout<<endl;
		#endif
	}

	return 0;
}

其实这题有更Easy的解法……贪心!

如果我们把原序列看成一条直线,且另a[0]和a[n+1]=-INF

那么如图所示


显然当最后的折现向下时:


显然解为凸点数*2

而当最后的折线向上时:



解为凸点数*2-1

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
#define MAXN (30000+10)
int tt,n,a[MAXN];
int main()
{
	scanf("%d",&tt);
	while (tt--)
	{
		scanf("%d",&n);
		int ans=0;
		a[0]=a[n+1]=-0xfffffff;
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		for (int i=1;i<=n;i++) if (a[i-1]<a[i]&&a[i]>a[i+1]) ans++;
		ans*=2;
		if (a[n-1]<a[n]) ans--;
		cout<<ans<<endl;
	}

	return 0;
}