OVa Online Judge 学习笔记- AOAPC I: Volume 2. Data Structure Lists

一、题目

OVa Online Judge 学习笔记- AOAPC I: Volume 2. Data Structure Lists_第1张图片

二、C++ STL Container

原因:避免自己构造相应的list,包括静态存储结构,链式存储结构。

1.数组 array:

Arrays are fixed-size sequence containers: they hold a specific number of elements ordered in a strict linear sequence.

优点:快速访问存取。

Container properties:

Sequence

Contiguous storage

Fixed-size aggregate

2.向量 vector && vector:

Vectors are sequence containers representing arrays that can change in size.

优点:快速访问存取。

Container properties:

Sequence

Dynamic array

Allocator-aware

3.双向链表 list:

Lists are sequence containers that allow constant time insert and erase operations anywhere within the sequence, and iteration in both directions.

优点:快速插入删除操作。

Container properties:

Sequence

Doubly-linked list

Allocator-aware

4.前向链表 forward_list:

Forward lists are sequence containers that allow constant time insert and erase operations anywhere within the sequence.

优点:快速插入删除操作。

Container properties:

Sequence

linked list

Allocator-aware

5.堆栈 stack:

Stacks are a type of container adaptor, specifically designed to operate in a LIFO context (last-in first-out), where elements are inserted and extracted only from one end of the container.

  • empty
  • size
  • back
  • push_back
  • pop_back

特点:LIFO。

6.队列

1)queue

queues are a type of container adaptor, specifically designed to operate in a FIFO context (first-in first-out), where elements are inserted into one end of the container and extracted from the other.

  • empty
  • size
  • front
  • back
  • push_back
  • pop_front

特点:FIFO。

2) 优先队列:priority_queue

Priority queues are a type of container adaptors, specifically designed such that its first element is always the greatest of the elements it contains, according to some strict weak ordering criterion.

This context is similar to a heap, where elements can be inserted at any moment, and only themax heap element can be retrieved (the one at the top in thepriority queue).

特点:heap。

  • empty()
  • size()
  • front()
  • push_back()
  • pop_back()

7.双端队列

deque (usually pronounced like "deck") is an irregular acronym of double-ended queue. Double-ended queues are sequence containers with dynamic sizes that can be expanded or contracted on both ends (either its front or its back).

Container properties:

Sequence

Dynamic array

Allocator-aware

三、做题笔记

1.127 - "Accordian" Patience

知识点:

1.扑克的堆叠操作只能是最上方,就像堆栈一样,因此可以用stack数据结构。

2.扑克的序列相当于上述各个堆栈形成的线性表,涉及到元素的访问及删除两种操作,可以用vector>数据结构。

注意:

1.warning:

warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

原因循环里面和size()比较时出现,因为size返回值是unsigned integer型,而循环计数一般采用int型,改为size_t型可以解除该warning:

for (size_t i = 0; i < cards.size(); i++) {

2.error:

error: ‘>>’ should be ‘> >’ within a nested template argument list
vector> cards;

采用g++ -std=c++11 新标准时并不会报错,旧标准会出错。

3.对问题理解:

程序一直没跑出例子的结果,后面通过各种可能性测试发现是以下语义理解错误:

Whenever the card matches its immediate neighbour on the left, or matches the third card to the left, it may be moved onto that card. 

开始采用的方法,是优先移动到immediate neighbour on the left,程序结果出错。

后面改为优先移动到 the third card to the left,程序结果正确。

这里可以发现问题中“it"单词是加粗的,以后可以慎重考虑这些加粗的单词并正确理解题意,按照上面那句话意思,仔细思考,确实存在优先移动到第三张card的含义。

4.STL container

有些方法,是C++11标准才有的,使用需加入std=c++11编译选项,这部分可以通过C++ reference网站进行参考,网站在讲解时会有这些提示。

5.测试工具:

可以自己生成测试input.txt文件,输出后与http://uvatoolkit.com/problemssolve.php 输入该input.txt文件后进行比对,看结果对不对。

这个适用于一些特殊的数据,需要参考输出时或UVAOJ网站连接不顺畅时使用。

做题记录:TL -> AC

1.Time limit exceeded

处理:由于字符串最多只有两个,string 改用 char[3],开辟一个全局变量char a[52][3]存储所有数据,并且stack只存储指向正确位置的指针。

修改这部分后获得AC。

代码:

/**
 * @file id_127.cpp
 * @brief AOAPC I 127
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-03-27
 */

#include 
#include 
#include 
#include 

using namespace std;

bool pile(const char* a, const char* b)
{
    return (a[0] == b[0] || a[1] == b[1]);
}

char single_card[52][3];

int main()
{
    vector< stack > cards;
    int single_card_index = 0;
    while (cin >> single_card[single_card_index] &&
            single_card[single_card_index][0] != '#') {
        // add pile to card
        stack single_pile;
        single_pile.push(single_card[single_card_index]);
        cards.push_back(single_pile);
        single_card_index++;
        // proccess a group until spectial count
        if (cards.size() != 52)
            continue;
        int finish_flag = 1;
        int remain = cards.size();
        while (finish_flag) {
            finish_flag = 0;
            for (int i = 0; i < remain; i++) {
                int pile_flag = 1;
                while (pile_flag) {
                    pile_flag = 0;
                    if (i > 2 && pile(cards[i].top(), cards[i - 3].top())) {
                        cards[i - 3].push(cards[i].top());
                        cards[i].pop();
                        if (cards[i].empty()) {
                            cards.erase(cards.begin() + i);
                            remain--;
                        }
                        i -= 3;
                        pile_flag = 1;
                        finish_flag = 1;
                        continue;
                    }
                    if (i > 0 && pile(cards[i].top(), cards[i - 1].top())) {
                        cards[i - 1].push(cards[i].top());
                        cards[i].pop();
                        if (cards[i].empty()) {
                            cards.erase(cards.begin() + i);
                            remain--;
                        }
                        i--;
                        pile_flag = 1;
                        finish_flag = 1;
                        continue;
                    }
                }
            }
        }
        if (remain != 1)
            cout << remain << " piles remaining:";
        else
            cout << remain << " pile remaining:";
        for (size_t i = 0; i < cards.size(); i++) {
            cout << " " << cards[i].size(); 
        }
        cout << endl;
        // clear data
        single_card_index = 0;
        cards.clear();
    }
    return 0;
}

2.101 - The Blocks Problem

知识点:

C++:

1.第一种方法:建立一个标记block的结构体数组,记录每个block所在的block position位置以及block在该位置的排名。

这样,查询每个block的状态时间复杂度为O(n)。再根据操作要求进行操作。麻烦的地方在于处理pile命令时,取出该pile后还得根据不同block在排位值排名进行排序以保证次序不变,这里使用map快速解决。

2.第二种方法:建立一个记录block position状态的vector< vector >结构,每个position又由vector维护该位置所有block的排名及标号。

这样,查询每个block的状态时间复杂度为O(n^2)。但是插入删除操作很快,因为他们总是有序的,处理pile时可以用queue快速处理。

做题记录:WA -> AC

1.正确理解题目含义!!!如果不肯定可以利用uvatookit测试!!!!!!!看清题目每一句话的意思,不要忽略重要信息并自己假定逻辑。

这里理解题目出错导致结果错误,虽然例子结果恰好一致,但是利用uvatookit很容易发现别的输入下就出错了。

代码:

/**
 * @file id_101.cpp
 * @brief AOAPC I 101
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-03-27
 */

#include 
#include 
#include 
#include 

using namespace std;

vector< vector > blocks;

int main()
{
    // get block numbers < 25
    int num_blocks;
    cin >> num_blocks;
    cin.get();
    for (int i = 0; i < num_blocks; i++) {
        vector block;
        block.push_back(i);
        blocks.push_back(block);
    }
    string command;
    int value_a;
    int value_b;
    string position;
    while (cin >> command && command != "quit") {
        cin >> value_a >> position >> value_b;
#ifndef ONLINE_JUDGE
        cout << command << value_a << position << value_b << endl;
#endif
        cin.get();
        queue data;
        // ignore illegal command
        if (value_a == value_b) continue;
        int blocks_a, blocks_b;
        int block_a, block_b;
        for (size_t i = 0; i < blocks.size(); i++) {
            for (size_t j = 0; j < blocks[i].size(); j++) {
                if (blocks[i][j] == value_a) {
                    blocks_a = i;
                    block_a = j;
                }
                if (blocks[i][j] == value_b) {
                    blocks_b = i;
                    block_b = j;
                }
            }
        }
        if (blocks_a == blocks_b) continue;
        // move and pile
        if (command == "move") {
            // move a only
            data.push(value_a);
            // return any blocks that are stacked on top of a to their
            // initial position
            for (size_t k = block_a + 1; k < blocks[blocks_a].size(); k++) {
                int i = blocks[blocks_a][k];
                blocks[i].push_back(i);
            }
        } else {
            // pile a
            for (size_t k = block_a; k < blocks[blocks_a].size(); k++)
                data.push(blocks[blocks_a][k]);
        }
        // remove old data of a
        blocks[blocks_a].erase(blocks[blocks_a].begin() + block_a,
                blocks[blocks_a].end());
#ifndef ONLINE_JUDGE
        cout << "data:" << data.size() << endl;
#endif
        // onto and over
        if (position == "over") {
            // onto the top of stack containing block b
            while (!data.empty()) {
                blocks[blocks_b].push_back(data.front());
                data.pop();
            }
        } else {
            // return any blocks that are stacked on top of b to their
            // initial position
            for (size_t k = block_b + 1; k < blocks[blocks_b].size(); k++) {
                int i = blocks[blocks_b][k];
                blocks[i].push_back(i);
            }
            // clear
            blocks[blocks_b].erase(blocks[blocks_b].begin() + block_b + 1,
                    blocks[blocks_b].end());
        }
        // refresh new data of a
        while (!data.empty()) {
            blocks[blocks_b].insert(blocks[blocks_b].begin() + block_b + 1,
                    data.front()); 
            data.pop();
        }
    }
    // output result
    for (size_t i = 0; i < blocks.size(); i++) {
        cout << i << ":";
        for (size_t j = 0; j < blocks[i].size(); j++) {
            cout << " " << blocks[i][j];
        }
        cout << endl;
    }
    return 0;
}

3.133 - The Dole Queue MARK

双向约瑟夫环

知识点:循环链表的索引定位以及链表插入删除操作。

技巧:

1.循环circlr的索引加入偏移量求新索引,单用求余可能会错,因为偏移可能大于一圈,导致求余时仍然为负值。

优化前出错: k_index = (k_index + N) % N;

优化后正确输出:

while (k_index < 0) k_index += N;
while (k_index > N) k_index -= N;

2.闭环时索引要考虑为零的情况(假设索引值1-N)

while (k_index < 0) k_index += N;
while (k_index > N) k_index -= N;
while (m_index < 0) m_index += N;
while (m_index > N) m_index -= N;
if (k_index == 0) k_index = N;
if (m_index == 0) m_index = N;

3.索引时确定以下标为1开始索引还是0开始索引,不要弄混了。

4.删除多个结点时需要考虑先删除的结点对后删除的结点的影响!!!!这里有删除先后顺序问题,如果索引值都不变情况下同时删除,应该先删除靠后的结点,在删除靠前的结点。

C++:

1.使用vector操作数据,原因:方便索引及删除操作。

2.cout 输出流格式化:(与printf对比学习) 头文件

这里解决问题时使用到的:setw(3)(->%3d) 代码:cout << setw(3) << first_remove;

拓展:输出格式控制

(参考:http://c.biancheng.net/cpp/biancheng/view/116.htmlhttp://c.biancheng.net/cpp/biancheng/view/2227.html)

对输出格式的控制,既可以用控制符,也可以用cout流的有关成员函数,二者的作用是相同的。

1.使用控制符控制输出格式:

输入输出流的控制符
控制符 作 用
dec 设置数值的基数为10
hex 设置数值的基数为16
oct 设置数值的基数为8
setfill(c) 设置填充字符c,c可以是字符常量或字符变量
setprecision(n) 设置浮点数的精度为n位。在以一般十进制小数形式输出时,n代表有效数字。在以fixed(固定小数位数)形式和 scientific(指数)形式输出时,n为小数位数
setw(n) 设置字段宽度为n位
setiosflags( ios::fixed) 设置浮点数以固定的小数位数显示
setiosftags( ios::scientific) 设置浮点数以科学记数法(即指数形式)显示
setiosflags( ios::left) 输出数据左对齐
setiosflags( ios::right) 输出数据右对齐
setiosflags( ios::skipws) 忽略前导的空格
setiosflags( ios::uppercase) 数据以十六进制形式输出时字母以大写表示
setiosflags( ios::lowercase) 数据以十六进制形式输出时宇母以小写表示
setiosflags(ios::showpos) 输出正数时给出“+”号

需要注意的是: 如果使用了控制符,在程序单位的开头除了要加iostream头文件外,还要加iomanip头文件。

例子:

cout<

2.用流对象的成员函数控制输出格式


用于控输出格式的流成员函数
流成员函数 与之作用相同的控制符 作用
precision(n) setprecision(n) 设置实数的精度为n位
width(n) setw(n) 设置字段宽度为n位
fill(c) setfill(c) 设置填充宇符c
setf() setiosflags() 设置输出格式状态,括号中应给出格式状态,内容与控制符setiosflags括号中的内容相同,如表13.5所示
unsetf() resetioflags() 终止已设置的输出格式状态,在括号中应指定内容


设置格式状态的格式标志
格式标志 作用
ios::left 输出数据在本域宽范围内向左对齐
ios::right 输出数据在本域宽范围内向右对齐
ios::internal 数值的符号位在域宽内左对齐,数值右对齐,中间由填充字符填充
ios::dec 设置整数的基数为10
ios::oct 设置整数的基数为8
ios::hex 设置整数的基数为16
ios::showbase 强制输出整数的基数(八进制数以0打头,十六进制数以0x打头)
ios::showpoint 强制输出浮点数的小点和尾数0
ios::uppercase 在以科学记数法格式E和以十六进制输出字母时以大写表示
ios::showpos 对正数显示“+”号
ios::scientific 浮点数以科学记数法格式输出
ios::fixed 浮点数以定点格式(小数形式)输出
ios::unitbuf 每次输出之后刷新所有的流
ios::stdio 每次输出之后清除stdout, stderr

cout流的成员函数是在头文件iostream 中定义的,因此只需包含头文件iostream,不必包含iomanip。

例子:
cout.setf(ios::showbase);//显示基数符号(0x或)
cout.unsetf(ios::dec); //终止十进制的格式设置

注意:

1.成员函数width(n)和控制符setw(n)只对其后的第一个输出项有效。

2.在表13.5中的输出格式状态分为5组,每一组中同时只能选用一种(例如dec、hex和oct中只能选一,它们是互相排斥的)。在用成员函数setf和控制符setiosflags设置输出格式状态后,如果想改设置为同组的另一状态,应当调用成员函数unsetf(对应于成员函数self)或resetiosflags(对应于控制符setiosflags),先终止原来设置的状态。然后再设置其他状态,大家可以从本程序中看到这点。程序在开始虽然没有用成员函数self和控制符setiosflags设置用dec输出格式状态,但系统默认指定为dec,因此要改变为hex或oct,也应当先用unsetf 函数终止原来设置。如果删去程序中的第7行和第10行,虽然在第8行和第11行中用成员函数setf设置了hex和oct格式,由于未终止dec格式,因此hex和oct的设置均不起作用,系统依然以十进制形式输出。

3.用setf 函数设置格式状态时,可以包含两个或多个格式标志,由于这些格式标志在ios类中被定义为枚举值,每一个格式标志以一个二进位代表,因此可以用位或运算符“|”组合多个格式标志。如倒数第5、第6行可以用下面一行代替:
cout.setf(ios::internal | ios::showpos); //包含两个状态标志,用"|"组合


做题记录:AC

1.通过uvatoolkit 网站测试用例,把自己的程序结果与该网站给出的结果进行对比,由小寻找到程序的BUG。

2.做题时要把索引值和数据分开思考,虽然数据可能时连续的,但跟索引值意义完全不同,不分清这点很容易陷入思维误区。

3.设计好数据结构后先考虑实现正确解决问题,再考虑优化问题,第一次提交的版本虽然获得AC,但是明显有优化空间,里面有不少代码重复并且逻辑可以优化。

4.具有相关关系的量,用同一个值表示,不要同时去操作两个,这里指N和circle.size()。

5.获得AC后google浏览别的代码学习。

代码:

/**
 * @file id_133.cpp
 * @brief AOAPC I 133
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-03-28
 */

#include 
#include 
#include 
#include 

using namespace std;

int main()
{
    // get N, k m
    int N, k, m;
    while (cin >> N >> k >> m && N != 0) {
        cin.get();
        // create circle
        vector circle;
        for (int i = 1; i <= N; i++)
            circle.push_back(i);
        // initialize index (index begin from 1 to N)
        int k_index = k;
        int m_index = N + 1 - m;
        while (N) {
            while (k_index < 0) k_index += N;
            while (k_index > N) k_index -= N;
            while (m_index < 0) m_index += N;
            while (m_index > N) m_index -= N;
            if (k_index == 0) k_index = N; 
            if (m_index == 0) m_index = N;
#ifndef ONLINE_JUDGE
            cout << "next " << k_index << " : " << m_index << endl;
#endif
            int first_remove = circle[k_index - 1];
            int second_remove = circle[m_index - 1];
            if (k_index != m_index) {
                if (k_index < m_index) {
                    // erase in sequence
                    circle.erase(circle.begin() + m_index - 1);
                    circle.erase(circle.begin() + k_index - 1);
                    // refresh index
                    k_index += k - 1;
                    m_index -= m + 1;
                } else {
                    // erase in sequence
                    circle.erase(circle.begin() + k_index - 1);
                    circle.erase(circle.begin() + m_index - 1);
                    // refresh index
                    k_index += k - 2;
                    m_index -= m;
                }
                // output
                cout << setw(3) << first_remove
                    << setw(3) << second_remove;
            } else {
                // erase
                circle.erase(circle.begin() + k_index - 1);
                // refresh index
                k_index += k - 1;
                m_index -= m;
                // output
                cout << setw(3) << first_remove;
            }
            // refresh circle size
            N = circle.size();
            if (N) cout << ",";
#ifndef ONLINE_JUDGE
            cout << "after dec: " << k_index << ","
                << m_index << "," << N << endl;
            for (size_t i = 0; i < circle.size(); i++)
                cout << "remain " << i << ":" << circle[i] << endl;
#endif
        }
        // output result
        cout << endl;
    }
    return 0;
}

4.673 - Parentheses Balance

知识点:括号匹配,用stack解决。(计算器实现,stack使用的典型案例)

做题记录:WA -> AC

经过测试后,发现是读取问题,cin >> string 并没有正确处理空字符串,它会自动忽略空行,应该使用getline。

参考别人的代码后可采取的优化措施(未去实现):

1.如果string长度是奇数,那么是不可能匹配对的,因此可以快速输出NO;

2.长度为零时可以快速输出YES.

3.必不需要遍历整个string然后检查size,在发现push的内容是‘)’或‘]'却没有在stack匹配到时就可以判断出不可能匹配对,这时直接输出NO。

代码:

/**
 * @file id_673.cpp
 * @brief AOAPC I 673
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-03-29
 */

#include 
#include 
#include 
#include 

using namespace std;

bool ispair(char a, char b)
{
    return (a == '[' && b == ']') || (a == '(' && b == ')');
}
int main()
{
    int num_strings;
    cin >> num_strings;
    cin.get();
    string parantheses;
    while (num_strings--) {
        getline(cin, parantheses);
        stack parantheses_stack;
        for (int i = 0; i < parantheses.size(); i++) {
            if (parantheses_stack.empty()) {
                parantheses_stack.push(parantheses[i]);
            } else {
                if (ispair(parantheses_stack.top(), parantheses[i]))
                    parantheses_stack.pop();
                else
                    parantheses_stack.push(parantheses[i]);
            }
        }
        if (parantheses_stack.empty())
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

5.442 - Matrix Chain Multiplication

知识点:矩阵乘法匹配:stack

技巧:

1.矩阵数据包括行和列,可以建立结构体联立两个。

1.已知矩阵用字母标记,可以利用map标记记录每个矩阵信息。

也可以数组实现,效率更高:由于字母只有'A'-'Z',可以存储到长度为26的结构体数组中,对字母x利用(x - 'A')计算偏移即可获得x在结构体数组中下标位置。

2.由于矩阵相乘总是成对的出现的,排除输入为单矩阵这种情况,在其他情况下,我们并不需要通过对‘(’和‘)’进行压栈出栈记录位置信息(利用堆栈实现计算器程序这种例子是必需的),每次只需要获得栈顶2个矩阵即可(pop两次)。

拓展:EBNF语法学习(阅读题目或定义符号用到)

Extended Backus–Naur Form

(参考:http://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form)

Backus–Naur Form

(参考:http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form)

Table of symbols

Usage Notation
definition =
concatenation ,
termination  ;
termination . [1]
alternation |
option [ ... ]
repetition { ... }
grouping ( ... )
terminal string " ... "
terminal string ' ... '
comment (* ... *)
special sequence  ? ... ?
exception -

做题记录: AC

6.11111 - Generalized Matrioshkas

知识点:stack。

从行中提取不定长的数组(包含正负):

A.C语言实现:

格式化IO:

char* line + fgets/gets + sscanf (, 获得line长度)

C实现atoi函数:

char* line + fgets/gets + atoi

B.C++ string + atoi

string + string.data() + offset + atoi

做题记录: AC

7.11234 Expressions MARK

知识点:考察堆栈和队列。考察二叉树使用及不同遍历方法。

技巧:表达式的识别,前缀表达式、中缀表达式、后缀表达式的获得方法,解析。

做题记录:TL -> TL -> WA -> AC

1.先用堆栈对表达式进行解析成表达式,再进行逆解析退回到队列中,逆解析出来,超时。

2.参考别人的文章:题目要求重新写出给出的后缀表达式,使原后缀表达式的栈方法和新表达式的队列方法表示的表达式相同,也就是两种表达式对应的表达式树相等。

对比自己的思路可以发现,这道题我解析堆栈形式的后缀表达式时,翻译成人类的语言,加括号,再进行逆解析,别人使用的是树结构,不用考虑人看不看得懂,关键是存储非常方便,这到题我思路主要局限在栈和队列的使用,并没有很好的使用树这种结构。

思路指引:

题目的最关键部分是进行二叉树建树, 以及层次遍历逆序输出,还有利用栈的“括号匹配”思想。 二叉树的基本结构是,父结点都是操作符,子节点都是数字。 对于给出的序列, 从左到右遍历,遇到代表数字的小写则建立一个无儿子的树,然后把根结点指针入栈, 遇到代表操作符的大写字母,则从栈中弹出两个根结点,然后建立一个以大写字母为根,弹出的两个操作数为左右儿子的树,再把这个新树的根结点指针压入栈。如此循环下去。 最后,在栈顶的那个指针就是最后建成的树的根结点。 然后对这颗树进行层次遍历把字母取出来,最后逆序输出即可。

1.利用堆栈建立树,叶子结点为操作数,非叶子结点为操作符。

2.利用队列对树进行层次遍历。

其他:每行字符串较长,采用cstdio的gets函数读取。

8.10050 - Hartals

知识点:数组

技巧:

1.采用位图数据结构

2.while循环,加法替代不断乘以相同系数的大批量乘法运算。速度优化:

旧:

int length = days / hartal + 1;
for (int i = 1; i <= length; i++)
day_log[1 + i * hartal] = 1;

新:

int length = hartal;
while (hartal <= days) {
day_log[1 + hartal] = 1;
hartal += length;
}

做题记录: AC

9.540 - Team Queue

知识点:根据数据范围及提点,利用位图数据结构,建立关联数组实现常数时间访问;数组队列实现。

特点:这道题要通过位图数据结构实现常量时间的处理。

做题记录: RE -> 尚未解决。

10.10152 - ShellSort MARK

关键理解部分:

http://www.algorithmist.com/index.php/UVa_10152

It seems as though King Yertle has set a tough task for you, but trying a few examples on paper should lead you to the following insights:

  • A turtle doesn't have to move if it is already above all the turtles it is supposed to be above. Otherwise, it must move.
  • If a turtle moves to the top, then the turtle that is supposed to be directly above it must also move.
  • Applying this recursively, this means if turtle  must move, then turtles  must also move, in that order.

So, the problem boils down to finding the lowest (in terms of placement on the wanted order of the stack) turtle that must move, and printing its name and the name of every turtle above it in the wanted ordering of the stack.

没有理清思路的地方:思考序列如何选择turtle什么时候移动,我们通过堆栈可以快速判断出要移动的turtle,但是自己没思路去判断,怎么选择这些顺序。认真考虑上面哪句话的含义,在确定次序时,如果我们先移动小的x turtle再移动大的X turtle,那么小的x turtle必然需要再移动一次,因为X turtle移动后,比他要小的x turtle必然需要再移动一次才能使队列正确排序,因此,我们可以得出结论,确定所有要移动的turtle后,按次序先移动大turtle再移动小turtle即可。(假设目标时turtle从小到大排列)

这种思考方式:通过选择对象之间的相互关系或作用效果分析不同选择方式对问题造成的影响,从而寻找最优解。

其他:

如果线性表设计到较少的数据删除和插入,string并不总是需要用vector容器,数组形式的string a[100]效率更高,本题如此。

针对这个问题,stack可以被索引/指针下表指示法高效替代,效率更高。

做题记录: google -> AC

拓展:

1.本题的排序方法跟通常定义的希尔排序算法并不一样,希尔排序见:http://blog.csdn.net/chensilly8888/article/details/42591861

希尔排序是插入排序的变种。

插入排序用的比较的元素总是以距离一为单位减小并逐个比较,而希尔排序使用的这里距离gap是变化的,它逐渐减小这个gap并通过对序列进行多次循环从而进行排序。



其他总结:

1.使用stack,要考虑好push和pop的数据什么才是最好的,设计好这一步,可以简化算法并有效利用stack。

代码:

https://github.com/chenxilinsidney/funnycprogram/tree/master/acm/aoapc



你可能感兴趣的:(ACM,编程思想,算法)