2016年兰州交通大学校园选拔赛

本文是2016年兰州交通大学校园选拔赛的题目解析,涵盖多种编程问题,包括人民币兑换、全排列、符号配对、中位数计算、单词排序、欧拉回路和热门话题等。作者分享了思路和AC代码,反思了在欧拉回路和地宫搜索题目中的不足,期待接下来的竞赛能有更好表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


时间:2016.11.27   9:00——12:00

OJ系统:PTA(感谢陈越姥姥提供竞赛平台~)

成绩:144/220(分数/总分)


自我评价:及格,打个分数的话,60%吧......今天状态一直不好,昨晚发烧+失眠......唉 :-( 


以下是题目和代码。(做法可能不是最优的,路过的童鞋们多多指教~)



人民币兑换   (15分)

1元5角钱人民币兑换5分、2分和1分的硬币(每一种都要有)共100枚,会有很多种兑换方案。请编写程序给出各种兑换方案。

输入格式:

输入为一个正整数n,表示要求输出前n种可能的方案。方案的顺序,是按照5分硬币从少到多排列的。

输出格式:

显示前n种方案中5分、2分、1分硬币各多少枚。每行显示一种方案,数字之间空一格,最后一个数字后没有空格。

注意:如果全部方案不到n种,就顺序输出全部可能的方案。

输入样例:

5

输出样例:

1 46 53  
2 42 56  
3 38 59  
4 34 62  
5 30 65


思路分析:这道题类似于经典的“百钱百鸡”,暴力枚举即可。定义five、two、one分别是5分、2分和1分的硬币,因为每种硬币数量都有且总的数量是100枚,所以三个变量的下限都是1~100之间。把1元5角单位转化成分,即150分,枚举三个变量找到解就输出,退出函数。


Accepted Code:

#include <cstdio>

#define TARGET 1 * 100 + 5 * 10

void solve( int cur ) {
    for( int five = cur; five < 100; five++ ) {
        for( int two = 1; two < 100; two++ ) {
            for( int one = 1; one < 100; one++ ) {
                if( ( five * 5 + two * 2 + one ) == TARGET ) {
                    if( five + two + one == 100 ) {
                        printf( "%d %d %d\n", five, two, one );
                        return;
                    }
                }
            }
        }
    }
}

int main() {
    int n;

    scanf( "%d", &n );

    for( int i = 1; i <= n; i++ ) {
        solve( i );
    }

    return 0;
}



输出全排列   (20分)

请编写程序输出前nn个正整数的全排列(n<10n<10),并通过9个测试用例(即nn从1到9)观察nn逐步增大时程序的运行时间。

输入格式:

输入给出正整数nn<10<10)。

输出格式:

输出1到nn的全排列。每种排列占一行,数字间无空格。排列的输出顺序为字典序,即序列{ a_1, a_2, \cdots, a_n }a1,a2,,an排在序列{ b_1, b_2, \cdots, b_n }b1,b2,,bn之前,如果存在kk使得a_1=b_1, \cdots, a_k=b_ka1=b1,,ak=bk 并且 a_{k+1}<b_{k+1}ak+1<bk+1

输入样例:

3

输出样例:

123
132
213
231
312
321

思路分析:很直白问你全排列。C++有写好的next_permutation函数,直接拿过来用就行。也可以自己写个DFS。用Java的童鞋就比较悲催了,不仅要自己写DFS还要考虑超时的问题......


Accepted Code:

#include <cstdio>
#include <algorithm>

#define MAX 10

using namespace std;

int a[MAX];
int b[MAX];
int visit[MAX];

void Print( int n ) {
    for( int i = 0; i < n; i++ ) {
        printf( "%d", a[i] );
    }
    printf( "\n" );
}

// DFS全排列,不要数组a,b也行,我这里写有点麻烦了
// cur是已经选择数字的数量
// n是需要的数字数量,递归出口
void dfs( int cur, int n ) {
    if( cur == n ) {
        for( int i = 0; i < n; i++ ) {
            printf( "%d", b[i] );
        }
        printf( "\n" );
        return;
    }

    for( int i = 0; i < n; i++ ) {
        if( !visit[i] ) {
            visit[i] = 1;
            b[cur] = a[i];
            dfs( cur + 1, n );
            visit[i] = 0;   // 回溯
        }
    }
}

int main() {
    int n;

    scanf( "%d", &n );

    for( int i = 0; i < n; i++ ) {
        a[i] = i + 1;
    }

    // 把注释去掉用这个也可以
    /*
    do {
        Print( n );
    } while( next_permutation( a, a + n ) );
    */

    dfs( 0, n );

    return 0;
}


符号配对   (20分)

请编写程序检查C语言源程序中下列符号是否配对:/**/()[]{}

输入格式:

输入为一个C语言源程序。当读到某一行中只有一个句点.和一个回车的时候,标志着输入结束。程序中需要检查配对的符号不超过100个。

输出格式:

首先,如果所有符号配对正确,则在第一行中输出YES,否则输出NO。然后在第二行中指出第一个不配对的符号:如果缺少左符号,则输出?-右符号;如果缺少右符号,则输出左符号-?

输入样例1:

void test()
{
    int i, A[10];
    for (i=0; i<10; i++) /*/
        A[i] = i;
}
.

输出样例1:

NO
/*-?

输入样例2:

void test()
{
    int i, A[10];
    for (i=0; i<10; i++) /**/
        A[i] = i;
}]
.

输出样例2:

NO
?-]

输入样例3:

void test()
{
    int i
    double A[10];
    for (i=0; i<10; i++) /**/
        A[i] = 0.1*i;
}
.

输出样例3:

YES


思路分析:这道题没AC(拿了16分,一个测试点没过)。经典的使用栈的问题。策略是遇到 /*   (    [     { ,进行压栈,遇到*/   )   ]    }  就与栈顶元素进行判断,如果相同就执行弹栈,不相同说明格式是有问题的。这种策略是正确的,因为最近匹配的原因,位于栈顶的元素肯定是最近一次的符号。这道题没AC,多半原因是/*和*/的问题。


16分 Code:

#include <cstdio>
#include <stack>
#include <string>
#include <iostream>

using namespace std;

int main() {
	//freopen( "123.txt", "r", stdin );

	string input;
	stack<string> s;
	string ans;
	bool flag = true;

	while( 1 ) {
        getline( cin, input );
        if( input == "." || input == "\r" ) break;

        for( int i = 0; i < input.size(); i++ ) {
            if( input[i] == '(' ) s.push( "(" );
            if( input[i] == '[' ) s.push( "[" );
            if( input[i] == '{' ) s.push( "{" );
            if( ( input[i] == '/' && input[i + 1] == '*' ) || ( input[i] == '/' ) ) {
                s.push( "/*" );
            }

            string top;

            if( s.empty() ) top = "####";
            else top = s.top();
		// left not
            if( input[i] == ')' ) {
                if( top == "(" ) s.pop();
                else  {
                    flag = false;
                    ans = "?-)";
                }
            }
            if( input[i] == ']' ) {
                if( top == "[" ) s.pop();
                else  {
                    flag = false;
                    ans = "?-]";
                }
            }
            if( input[i] == '}' ) {
                if( top == "{" ) s.pop();
                else  {
                    flag = false;
                    ans = "?-}";
                }
            }
            if( ( input[i] == '*' && input[i + 1] == '/' ) || ( input[i] == '/' ) ) {
                if( top == "/*" ) s.pop();
                else  {
                    flag = false;
                    ans = "?-*/";
                }
            }

            if( !flag ) break;
        }
    }

	if( !s.empty() ) {
		ans = s.top();
		ans = ans + "-?";
		flag = false;
	}

	if( flag ) {
		cout << "YES" << endl;
	}
	else {
		cout << "NO" << endl;
		cout << ans << endl;
	}
	return 0;
}


两个有序序列的中位数   (25分)

已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列A_0, A_1, \cdots, A_{N-1}A0,A1,,AN1的中位数指A_{(N-1)/2}A(N1)/2的值,即第\lfloor(N+1)/2\rfloor(N+1)/2个数(A_0A0为第1个数)。

输入格式:

输入分三行。第一行给出序列的公共长度N(0<<N\le100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。

输出格式:

在一行中输出两个输入序列的并集序列的中位数。

输入样例1:

5
1 3 5 7 9
2 3 4 5 6

输出样例1:

4

输入样例2:

6
-100 -10 1 1 1 1
-50 0 2 3 4 5


思路分析:我认为这道题题意描述不清,当时赛场上调试了不少时间。题意中说两个序列的并集,我很快想到了用set,处理两个集合的并集,并找到中位数。然而提交WA,一个测试点没过。没想到是我想多了......直接合并两个序列(不去重,而两个集合的合并是去重的),排序找到中位数就OK。LeetCode上有很多类似的题,有更高效的做法。我的做法简单粗暴,也能AC~


Accepted Code:

#include <cstdio>
#include <algorithm>

#define MAX 100000 + 10

using namespace std;

int a[MAX];

int main() {
    int n;

    scanf( "%d", &n );

    for( int i = 0; i < 2 * n; i++ ) {
        scanf( "%d", &a[i] );
    }

    sort( a, a + 2 * n );

    printf( "%d\n", a[( 2 * n - 1 ) / 2] );

    return 0;
}


英文单词排序   (25分)

本题要求编写程序,输入若干英文单词,对这些单词按长度从小到大排序后输出。如果长度相同,按照输入的顺序不变。

输入格式:

输入为若干英文单词,每行一个,以#作为输入结束标志。其中英文单词总数不超过20个,英文单词为长度小于10的仅由小写英文字母组成的字符串。

输出格式:

输出为排序后的结果,每个单词后面都额外输出一个空格。

输入样例:

blue
red
yellow
green
purple
#

输出样例:

red blue green yellow purple


思路分析:亲爱的亲爱的,这道题为什么要用散列呢?显然是考排序呀~更准确的说是多标尺排序。第一标尺是单词长度,第二标尺是输入顺序。我的做题习惯是遇到这种多标尺排序问题,定义一个结构体存储信息,里边就是数据和标尺了。然后重写sort中的cmp函数就行,调用sort就可以了。注意输出格式,每个单词后面都有一个空格。


Accepted Code:

#include <iostream>
#include <string>
#include <algorithm>

#define MAX 20 + 10

using namespace std;

typedef struct {
    string str;
    int rank;   // 输入顺序
} STR;

STR str[MAX];

int cmp( STR a, STR b ) {
    int aLen = a.str.size();
    int bLen = b.str.size();
    
    // 判断第一标尺
    if( aLen != bLen ) {
        return aLen < bLen ? 1 : 0;
    }
    
    // 相同判断输入顺序
    return a.rank < b.rank ? 1 : 0;
}

int main() {
    string input;

    int i = 0;

    while( 1 ) {
        cin >> input;
        if( input == "#" ) {
            break;
        }
        else {
            str[i].str = input;
            str[i].rank = i;    // rank记录输入顺序
            i++;
        }
    }

    sort( str, str + i, cmp );

    for( int j = 0; j < i; j++ ) {
        cout << str[j].str << " ";
    }

    return 0;
}



哥尼斯堡的“七桥问题”   (25分)

哥尼斯堡是位于普累格河上的一座城市,它包含两个岛屿及连接它们的七座桥,如下图所示。

可否走过这样的七座桥,而且每桥只走过一次?瑞士数学家欧拉(Leonhard Euler,1707—1783)最终解决了这个问题,并由此创立了拓扑学。

这个问题如今可以描述为判断欧拉回路是否存在的问题。欧拉回路是指不令笔离开纸面,可画过图中每条边仅一次,且可以回到起点的一条回路。现给定一个无向图,问是否存在欧拉回路?

输入格式:

输入第一行给出两个正整数,分别是节点数NN (1\le N\le 10001N1000)和边数MM;随后的MM行对应MM条边,每行给出一对正整数,分别是该条边直接连通的两个节点的编号(节点从1到NN编号)。

输出格式:

若欧拉回路存在则输出1,否则输出0。

输入样例1:

6 10
1 2
2 3
3 1
4 5
5 6
6 4
1 4
1 6
3 4
3 6

输出样例1:

1

输入样例2:

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

输出样例2:

0


思路分析:欧拉回路问题,也就是小时候经常玩的一笔画问题。以前没研究过欧拉回路的问题......考场上花了点时间,最终拿了23分,没AC,好可惜......

因为要判断回路,我第一想法是拓扑排序,很快写好拓扑,提交WA,得了21分。然后又想了想用DFS访问边权,并删除掉访问过的边,防止回头。结果还是WA......然后又想着用并查集,很快写好并查集发现这并没什么用......最后DFS+TopSort一起上,DFS判断是否访问所有边,拓扑判断是否回路,拿了23分,最后一个case运行超时。


其实判断欧拉回路很简单:如果图中每个点的度是偶数,并且图是连通的,就是欧拉回路了......看来图的一些性质我还需要了解。


Accepted Code:

#include <cstdio>

#define MAX 1000 + 10

int MGraph[MAX][MAX];
int degree[MAX];
int visit[MAX];

void dfs( int v, int n ) {
    for( int i = 1; i <= n; i++ ) {
        if( !visit[i] && MGraph[v][i] != 0 ) {
            visit[i] = 1;
            dfs( i, n );
        }
    }
}

int main() {
    int n, m;

    scanf( "%d%d", &n, &m );

    int a, b;

    for( int i = 0; i < m; i++ ) {
        scanf( "%d%d", &a, &b );
        MGraph[a][b] = 1;
        MGraph[b][a] = 1;
        degree[a]++;
        degree[b]++;
    }

    int cnt = 0;
    for( int i = 1; i <= n; i++ ) {
        if( !visit[i] ) {
            dfs( i, n );    // dfs计算连通块的数量,如果是1则图中所有点在一个连通块中
            cnt++;
        }
    }

    bool flag = true;

    if( cnt != 1 ) {
        flag = false;
    }

    // 判断所有点的度是否为偶数
    for( int i = 1; i <= n; i++ ) {
        if( degree[i] % 2 != 0 ) {
            flag = false;
            break;
        }
    }

    if( flag ) {
        printf( "1\n" );
    }
    else {
        printf( "0\n" );
    }

    return 0;
}


新浪微博热门话题   (30分)

新浪微博可以在发言中嵌入“话题”,即将发言中的话题文字写在一对“#”之间,就可以生成话题链接,点击链接可以看到有多少人在跟自己讨论相同或者相似的话题。新浪微博还会随时更新热门话题列表,并将最热门的话题放在醒目的位置推荐大家关注。

本题目要求实现一个简化的热门话题推荐功能,从大量英文(因为中文分词处理比较麻烦)微博中解析出话题,找出被最多条微博提到的话题。

输入格式:

输入说明:输入首先给出一个正整数NN\le 10^5105),随后NN行,每行给出一条英文微博,其长度不超过140个字符。任何包含在一对最近的#中的内容均被认为是一个话题,如果长度超过40个字符,则只保留前40个字符。输入保证#成对出现。

输出格式:

第一行输出被最多条微博提到的话题,第二行输出其被提到的微博条数。如果这样的话题不唯一,则输出按字母序最小的话题,并在第三行输出And k more ...,其中k是另外几条热门话题的条数。输入保证至少存在一条话题。

输入样例:

4
This is a #test of topic#.
Another #Test of topic.#
This is a #Hot# #Hot# topic
Another #hot!# #Hot# topic

输出样例:

Hot
2
And 1 more ...


思路分析:读了题目,描述是看懂了,就是样例看不懂,尤其那句And k more......是怎么判断的???网上查别人的题解,也没人AC,估计是题目有问题吧。这里说下大概的方法:

1.首先处理输入问题,因为有空格,所以按行输入处理。C\C++中可以使用gets()或getline( cin, string )处理输入有空格的问题。

2.筛选出每行##之间的字符串,先判断这个字符串是否存在,方法可以用map记录已存在的字符串。不存在,就存到map中,存在记录数加1。注意这里的记录数加1不是这个字符串出现一次就加1,而是如果它在一条微博中出现过,则只能加1。例如第三行Hot出现了两次,Hot出现的次数只能加1,不能加2,因为他俩都在同一条微博中。

3.第2步就涉及到查找是否存在的问题,根据我的尝试直接使用map.find()搜索最后一个case超时,这就需要使用散列了。可以再开一个map记录每个字符串的id,id一定要是唯一的。或者根据字符串进行字符散列映射(链式分离法),不过这样有些复杂了。理论上再开一个map会更方便,应该能AC。


Code:无,gg



地下迷宫探索   (30分)

地道战是在抗日战争时期,在华北平原上抗日军民利用地道打击日本侵略者的作战方式。地道网是房连房、街连街、村连村的地下工事,如下图所示。

我们在回顾前辈们艰苦卓绝的战争生活的同时,真心钦佩他们的聪明才智。在现在和平发展的年代,对多数人来说,探索地下通道或许只是一种娱乐或者益智的游戏。本实验案例以探索地下通道迷宫作为内容。

假设有一个地下通道迷宫,它的通道都是直的,而通道所有交叉点(包括通道的端点)上都有一盏灯和一个开关。请问你如何从某个起点开始在迷宫中点亮所有的灯并回到起点?

输入格式:

输入第一行给出三个正整数,分别表示地下迷宫的节点数NN1<N\le 10001<N1000,表示通道所有交叉点和端点)、边数MM\le 30003000,表示通道数)和探索起始节点编号SS(节点从1到NN编号)。随后的MM行对应MM条边(通道),每行给出一对正整数,分别是该条边直接连通的两个节点的编号。

输出格式:

若可以点亮所有节点的灯,则输出从SS开始并以SS结束的包含所有节点的序列,序列中相邻的节点一定有边(通道);否则虽然不能点亮所有节点的灯,但还是输出点亮部分灯的节点序列,最后输出0,此时表示迷宫不是连通图。

由于深度优先遍历的节点序列是不唯一的,为了使得输出具有唯一的结果,我们约定以节点小编号优先的次序访问(点灯)。在点亮所有可以点亮的灯后,以原路返回的方式回到起点。

输入样例1:

6 8 1
1 2
2 3
3 4
4 5
5 6
6 4
3 6
1 5

输出样例1:

1 2 3 4 5 6 5 4 3 2 1

输入样例2:

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

输出样例2:

6 4 5 4 6 0


思路分析:一拿到这个题很开心呀,显然用DFS深搜,很快写好代码,提交WA,两个测试点错误......纠结了半个小时,找不出问题......

我的想法是这样的:从起点开始进行DFS,一直搜索,不回头。这样走到死胡同就无路可走了,DFS退出,把走过的路径记录到vector或数组或stack中,先正着打印一遍,再反向打印一遍,就相当于沿原路返回到起点。如果访问不了所有结点再额外打印一个0,为什么我提交WA呢?策略有问题吗?

网上搜了下别人的题解,和我想法不同的地方就是DFS的时候并添加返回路径,相当于在我的DFS基础上添加一行代码就OK。然后打印路径直接按顺序打印,不用反向打印。这种策略能AC。

我感觉一样的呀,,为什么我的不对呢,如果有路过的童鞋们明白帮我解答一下,感激不尽~~~


先上我的代码:


20分 Code

#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
#include <queue>
#include <cstdio>

#define MAX 3000 + 10
#define INF 0x3fffffff

using namespace std;

int MGraph[MAX][MAX];
int visit[MAX];

int n, m;
int S;
int cnt;

vector<int> vec;

void initMGraph() {
	for( int i = 0; i < MAX; i++ ) {
		for( int j = 0; j < MAX; j++ ) {
			MGraph[i][j] = 0;
		}
	}
}

// 一直深搜,不回头
void dfs( int cur ) {
	visit[cur] = 1;
	vec.push_back( cur );
	//cnt++;
	for( int i = 1; i <= n; i++ ) {
		if( !visit[i] && MGraph[cur][i] != 0 ) {
			dfs( i );
			//vec.push_back( cur );
			//cnt++;
		}
	}
}

int main() {
	initMGraph();
	//freopen( "123.txt", "r", stdin );
	scanf( "%d%d%d", &n, &m, &S );

	int a, b;
	for( int i = 0; i < m; i++ ) {
		scanf( "%d%d", &a ,&b );
		MGraph[a][b] = 1;
		MGraph[b][a] = 1;
	}

	//vec.push_back( S );
	//visit[S] = 1;
	//cnt = 1;
	dfs( S );

	bool flag = true;

	// 判断是否所有点都访问过了
	//if( cnt < n ) flag = false;

	for( int i = 1; i <= n; i++ ) {
        if( visit[i] == 0 ) {
            flag = false;
            break;
        }
	}

    // 先正着打印一次路径,再反向打印一次
	if( flag ) {
		for( int i = 0; i < vec.size(); i++ ) {
			if( i == 0 ) printf( "%d", vec[i] );
			else printf( " %d", vec[i] );
		}

		for( int i = vec.size() - 2; i >= 0; i-- ) {
			printf( " %d", vec[i] );
		}

	}
	else {
		for( int i = 0; i < vec.size(); i++ ) {
			if( i == 0 ) printf( "%d", vec[i] );
			else printf( " %d",vec[i] );
		}

		for( int i = vec.size() - 2; i >= 0; i-- ) {
			printf( " %d", vec[i] );
		}
		printf( " 0" );
	}


	return 0;
}


再贴上AC代码:


Accepted Code:

#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
#include <queue>
#include <cstdio>

#define MAX 3000 + 10
#define INF 0x3fffffff

using namespace std;

int MGraph[MAX][MAX];
int visit[MAX];

int n, m;
int S;
int cnt;

vector<int> vec;

void initMGraph() {
	for( int i = 0; i < MAX; i++ ) {
		for( int j = 0; j < MAX; j++ ) {
			MGraph[i][j] = 0;
		}
	}
}

// 一直深搜,不回头
void dfs( int cur ) {
	visit[cur] = 1;
	vec.push_back( cur );
	//cnt++;
	for( int i = 1; i <= n; i++ ) {
		if( !visit[i] && MGraph[cur][i] != 0 ) {
			dfs( i );
			vec.push_back( cur );
			//cnt++;
		}
	}
}

int main() {
	initMGraph();
	//freopen( "123.txt", "r", stdin );
	scanf( "%d%d%d", &n, &m, &S );

	int a, b;
	for( int i = 0; i < m; i++ ) {
		scanf( "%d%d", &a ,&b );
		MGraph[a][b] = 1;
		MGraph[b][a] = 1;
	}

	//vec.push_back( S );
	//visit[S] = 1;
	//cnt = 1;
	dfs( S );

	bool flag = true;
	for( int i = 1; i <= n; i++ ) {
        if( visit[i] == 0 ) {
            flag = false;
            break;
        }
	}
	if( flag ) {
        for( int i = 0; i < vec.size(); i++ ) {
            if( i == 0 ) printf( "%d", vec[i] );
            else printf( " %d", vec[i] );
        }
	}

	else {
        for( int i = 0; i < vec.size(); i++ ) {
            printf( "%d ", vec[i] );
        }
        printf( "0" );
	}


	return 0;
}




长城   (30分)

正如我们所知,中国古代长城的建造是为了抵御外敌入侵。在长城上,建造了许多烽火台。每个烽火台都监视着一个特定的地区范围。一旦某个地区有外敌入侵,值守在对应烽火台上的士兵就会将敌情通报给周围的烽火台,并迅速接力地传递到总部。

现在如图1所示,若水平为南北方向、垂直为海拔高度方向,假设长城就是依次相联的一系列线段,而且在此范围内的任一垂直线与这些线段有且仅有唯一的交点。

图 1

进一步地,假设烽火台只能建造在线段的端点处。我们认为烽火台本身是没有高度的,每个烽火台只负责向北方(图1中向左)瞭望,而且一旦有外敌入侵,只要敌人与烽火台之间未被山体遮挡,哨兵就会立即察觉。当然,按照这一军规,对于南侧的敌情各烽火台并不负责任。一旦哨兵发现敌情,他就会立即以狼烟或烽火的形式,向其南方的烽火台传递警报,直到位于最南侧的总部。

以图2中的长城为例,负责守卫的四个烽火台用蓝白圆点示意,最南侧的总部用红色圆点示意。如果红色星形标示的地方出现敌情,将被哨兵们发现并沿红色折线将警报传递到总部。当然,就这个例子而言只需两个烽火台的协作,但其他位置的敌情可能需要更多。

然而反过来,即便这里的4个烽火台全部参与,依然有不能覆盖的(黄色)区域。

图 2

另外,为避免歧义,我们在这里约定,与某个烽火台的视线刚好相切的区域都认为可以被该烽火台所监视。以图3中的长城为例,若A、B、C、D点均共线,且在D点设置一处烽火台,则A、B、C以及线段BC上的任何一点都在该烽火台的监视范围之内。

图 3

好了,倘若你是秦始皇的太尉,为不致出现更多孟姜女式的悲剧,如何在保证长城安全的前提下,使消耗的民力(建造的烽火台)最少呢?

输入格式:

输入在第一行给出一个正整数N(3 \le N \le 10^5105),即刻画长城边缘的折线顶点(含起点和终点)数。随后N行,每行给出一个顶点的xy坐标,其间以空格分隔。注意顶点从南到北依次给出,第一个顶点为总部所在位置。坐标为区间[-10^9, 10^9)[109,109)内的整数,且没有重合点。

输出格式:

在一行中输出所需建造烽火台(不含总部)的最少数目。

输入样例:

10
67 32
48 -49
32 53
22 -44
19 22
11 40
10 -65
-1 -23
-3 31
-7 59

输出样例:

2


这道题压根没看......我一直以为是上次天体赛决赛最后一题,那道题是考查凸包,暂时不会这个知识点,而且上次决赛最后一题也没人拿满,就干脆放弃了。天呐呐呐,结果是我记错了,凸包那个题是“水果忍者”,这道题是初赛的最后一题,当时也没几个人AC......所以,,干脆就不看了吧。




总结:除了微博那道题有点问题,还有最后一道压根没考虑做之外,剩下最多就能答160分了。遗憾的是欧拉回路和地宫搜索那两道没AC,着实可惜......再过10天又要上战场了,好担心......再好好复习一下!




                
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值