总结
CSDN竞赛越来越频繁了,这次竞赛比较简单,就当练习题了。
题目列表
1.求并集
题目描述
由小到大输出两个单向有序链表的并集 如链表 A {1 -> 2 -> 5 -> 7} 链表 B {3 -> 5 -> 7 -> 8} 输出: {1 -> 2 ->3 - > 5 -> 7 ->8} 。
输入描述:
第一行输入整数n,m,(1<=n,m<=1000)分别表示两个链表的长度。
第二行给出A链表所包含元素。(1<=a<=1000)
第三行给出B链表所包含元素。(1<=b<=1000)
输出描述:
按题目描述输出。
输入样例:
4 4
1 2 5 7
3 5 7 8
输出样例:
1 -> 2 ->3 -> 5 -> 7 ->8
分析
这道题作为面试题,考察链表比较合适,作为笔试题不太合适。一般的做法是使用类似于归并排序的合并算法,使用双指针来合并。但是完全没必要这么做,直接用一个数组读入,然后排下序就可以了。
当然,这题的用例告诉我们需要去重,所以可以读入一个set中,然后再排序就可以了。而实际上set已经默认排好序了,直接输出就可以了。
代码
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
set<int> s;
int main() {int n;int m;cin>>n>>m;vector<int> a;int x;for(int i = 0; i < n + m; i++) {cin>>x;s.insert(x);}for(auto t : s) {a.push_back(t);}int q = a.size();for(int i = 0; i < q - 1; i++) {cout<<a[i]<<" -> ";}cout<<a[q - 1]<<endl;return 0;
}
2.小T找糖豆
题目描述
已知连续序列A,包含1e18个元素,分别是[1,1e8]。 现在去除序列中的n个元素. 得到新的连续序列[X,1e8],该序列中 最小元素是?
分析
本题的题意是去掉若干元素后,求剩下连续序列的最小元素,用例给我们的感觉是必须是最后一个连续序列,所以相当于求出去除序列的最大值,加上1就是连续序列的最小元素了。
代码
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
int solution(int n, std::vector<int>& vec) {int res = 0;for(auto x : vec) {res = max(res, x);}return res + 1;
}
int main() {int n;std::vector<int> vec;std::cin>>n;std::string line_0, token_0;getline(std::cin >> std::ws,line_0);std::stringstream tokens_0(line_0);while(std::getline(tokens_0, token_0, ' ')) {vec.push_back(std::stoi(token_0));}int result = solution(n,vec);std::cout<<result<<std::endl;return 0;
}
3. 争风吃醋的豚鼠
题目描述
N个节点两两建边。 不存在3个节点相互之前全部相连(3个节点连接成环) 。最多能建立多少条边?
输入描述:
输入整数n.(1<=n<=1e5)
输出描述:
输出最大边数
输入样例:
4
输出样例:
4
分析
读完题目就知道肯定是有一个公式的,代码简洁,这类题目作为算法题不太合适,有点纯数学了。考试时想的是类似于二分图。我们将节点分为两组,两组节点之间互相全部连接上,组内节点不连接,这样添加任意一条边都会形成环,保证了边的最大值。
比如a,b,c,da,b,c,da,b,c,d四个点,a,ba,ba,b作为一组,c,dc,dc,d作为一组,连接ac,ad,bc,bdac,\ ad,\ bc,\ bdac, ad, bc, bd,构成的四条边不会形成环,同时边数是最大的。所以遍历下将nnn个节点划为两组节点的所有划法,取其中的边数最大值就可以了。
代码
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
int main() {int n;std::cin>>n;ll res = 0;for(int i = 1; i < n; i++) {res = max(res,(ll)i * (n - i));}std::cout<<res<<std::endl;return 0;
}
4.题目名称:选择客栈
题目描述
丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从 1 到 n 编号。每家客栈都按照某一种色调进行装饰(总共 k 种,用整数 0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。 两位游客一起去丽江旅 游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一 家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 p 。 他们想 知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 p 元的咖啡店小聚。
输入描述:
共n+1 行。
第一行三个整数 n ,k ,p ,每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值;
接下来的 n 行,第 i+1 行两个整数,之间用一个空格隔开,分别表示 i 号客栈的装饰色调和 i 号客栈的咖啡店的最低消费。
输出描述:
一个整数,表示可选的住宿方案的总数。
输入样例:
5 2 3
0 5
1 3
0 2
1 4
1 5
输出样例:
3
分析
如果说前面题目出的不像算法题,那么这最后一题就有点坑了,第一个坑在于不给数据范围,但凡审题的同学认真点都不会不给数据范围,这种题目不给数据范围的话,我们是不方便选择合适复杂度的算法来求解的。
首先要考虑的是选择的两个客栈中间有没有合适的咖啡店,我们固然可以通过遍历来判断,但是复杂度高。这里可以使用前缀和来优化下,咖啡店的最低消费具体是多少我们不关注,如果不超过p,就是1,表示可以选择;超过了p,就是0,不能选择。然后对这个由0和1构成的数组求下前缀和s,就可以在O(1)的时间内判断两个客栈之间有没有合适的咖啡店了。
客栈iii和jjj之间合适的咖啡店数是s[j]−s[i−1]s[j]-s[i-1]s[j]−s[i−1],只要这个值不是0,就表示我们可以选择i,ji,ji,j两个客栈住宿。
暴力的做法是二重循环枚举i,ji,ji,j,只要两家客栈色调相同,并且之间有合适的咖啡店,方案数就加一。时间复杂度是O(n2)O(n^2)O(n2),由于题目没有给定数据范围,所以可以提交下看看,发现TLE了,这就是本题的第二个坑,因为如果你在代码里面加上如果n>1e4n>1e4n>1e4的代码再提交就可以通过九成的用例了。这题用例的评测是一起了,最后一个用例TLE了,就一分没有,这点很不科学。
纯暴力的解法复杂度是立方级别的,前面使用前缀和优化掉一重循环,既然TLE了还需要继续优化。色调一共kkk种,虽然没有给数据范围,但是根据经验可以判定nnn的范围至少是10510^5105级别的,而kkk的范围一般不大。我们将所有的客栈根据色调不同按顺序分为kkk组,这样同一组内的客栈我们就不需要再考虑色调,只需要关心有没有合适的咖啡店就行了。问题就变成了在一个线性序列中,找到点对使得两点之间有合适的咖啡店了。
这里可以发现问题的单调性,比如一个人住在第一家客栈,第二个人从第二家客栈开始遍历,直到两家客栈之间有合适的咖啡店位置,从这家咖啡店到后面的所有客栈都可以住第二个人。然后第一个人住第二家客栈,第二个人不需要从第三家客栈遍历起,只需要从上次找到合适咖啡店的位置开始遍历就可以了,因为上次走过的客栈肯定都没有合适的咖啡店。这样一来,第二个人至多走完客栈一遍。如果某个色调序列的长度是q,那么在这个色调里面找两个合适客栈的复杂度就是O(q)O(q)O(q),遍历完k个色调序列的总的复杂度就是O(n)O(n)O(n)。
不考虑本题的数据范围和用例评测的坑,这题考察了前缀和和双指针算法,一步步优化时间复杂度,题目质量还是不错的。
代码
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
int main() {int n;int k;int p;cin>>n>>k>>p;vector<int> c(n+1), a(n+1), s(n+1, 0);vector<int> m[k];for(int i = 1; i <= n; i++) {cin>>c[i]>>a[i];if (a[i] > p) a[i] = 0;else a[i] = 1;s[i] = s[i-1] + a[i];m[c[i]].push_back(i);}int res = 0;for(int t = 0; t < k; t++) {int q = m[t].size();for(int i = 0,j = 0; i < q; i++) {if(a[m[t][i]]) {res += q - i - 1;continue;} else {if(j > i) {res += q - j;continue;} else j = i + 1;while(j < q && !(s[m[t][j]] - s[m[t][i]-1])) j++;if(j < q) {res += q - j;} else {break;}}}}cout<<res<<endl;return 0;
}