送自己一句话:即使生活很不顺利,也不要成为一个连自己都讨厌的人
那样,当你有一天回首过往,只不过是在演戏中匆匆做过这一生
题目:见我上传的资源
A:关于时间复杂度
解:
1.关键:
法一:手算,反正我们开始3个人就是每个人在草稿纸上令n =10^5手算 执行的语句的数量是否会超过10^8,虽然一开始就做对了,几分钟的事情,但是,我们提交的时候因为没有<<endl,所以提交错了4次,怎么说,哎,比赛,没办法,它也没说
法二:这是我们在纠结的过程中 其中锟同学 按照一个程序员的思维提出的,为什么不把这4段代码运行一下,看看执行次数呢? ---.....................真是,太。。。妙了
2.代码:
注:没有代码的。。
B:老实点,每次都有你
解:
1.关键:
(1)讲了很多故事,但是如果把故事的内容进行提炼的话,就是说 找出这一行数据中的 公因数,然后 从小到大 输出这个公因数的 所有因子
(2)注意,为了减少时间复杂度,我们考虑就是 就是从1-sqrt(n)中找因子i,另一个因子就是gcd/i了
然后存储到数组 ans,最后sort一下数组即可
2.代码如下:
int gcd(int a, int b)
{
//a > b
if (a < b)
{
swap(a, b);
}
while (a%b != 0)
{
int tmp = b;
b = a % b;
b = tmp;
}
return b;
}
int main()
{
int T;
cin >> T;
while (T--)
{
int n;
cin >> n; //输入n个整数
vector<int> arr(n, 0);
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
//(1)先利用依次 gcd,求解出公因数 common_num
int common_num = arr[0];
for (int i = 1; i < n; i++)
{
common_num = gcd(common_num, arr[i]);
}
cout << common_num << endl;
//(2)然后利用检查 <=sqrt(common_num) 中是否有 % == 0 的因子
vector<int> ans;
for (int i = 1; i <= sqrt((double)common_num); i++)
{
if (common_num % i == 0)
{
ans.push_back(i);
if(i != common_num/i)
ans.push_back(common_num / i); //防止重复
}
}
sort(ans.begin(), ans.end()); //排序
//然后输出
for (auto item : ans)
{
cout << item << " ";
}
cout << endl;
}
return 0;
}
C:木桶效应
解:
1.关键:
(1)我承认这个题目是 我的败笔,不过,人生有比这个更加令人忧伤的事情,所以,少年,从头开始,不必灰心,
(有的时候会有一些计较,但是,你想啊,单单和 广州计算机学院的同学就差距这么大了,更何况是浙江北京的那些同学呢,对吧,当你计较的时候,你已经输了,ok,心宽了就好)
(2)这个题目其实总的思路是,先得到二维数组vec_set(负责存放不同的桶子,筒子里面是元素),得到不同元素映射到的 桶子编号 map<x , index_bucket> ,然后对每个vec_set中的桶子进行sort,最后 分情况去输出1-n
(3)赛后想法:为了避免 合并2个桶子(因为后期可能出现 “桥梁元素”),我考虑使用图论的思想,先建图(得到一个二维数组graph作为邻接表),然后通过 bfs广搜得到一个个桶子中的所有元素,并且存放到cnt_bucket中
(4)说明一点,我用了一个 num_set来存储 爷爷提及的元素,这样最后可以单独把那些没有提及的元素 都单独作为新的bucket 放到 vec_set的末尾
2.代码如下:
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<algorithm>
#include<unordered_map>
#include<queue>
#include<unordered_set>
using namespace std;
int main()
{
int T;
cin >> T;
while (T--)
{
int n, m;
cin >> n >> m;
//算了,再利用一个set用来存储爷爷提及到的元素
unordered_set<int> num_set;
//一个大小为m的二维数组
vector<vector<int>> vec(m, vector<int>(2, 0));
//二维数组邻接表
vector<vector<int>> graph(n+1); //总共有n个节点 -input 1 10 --error
for (int i = 0; i < m; i++)
{
cin >> vec[i][0] >> vec[i][1];
int x = vec[i][0], y = vec[i][1];
//--记录爷爷提及到的 元素
if (num_set.count(x) == 0)
{
num_set.insert(x);
}
if (num_set.count(y) == 0)
{
num_set.insert(y);
}
graph[x].push_back(y); graph[y].push_back(x); //悟了,graph从下标0开始,所以需要n+1大小
}
//(1)利用1个二维数组vec_set 和 一个map<num , bucket_index>记录 木板对应的桶子的index
//先得到所有的 vec_set
vector<vector<int>> vec_set;
unordered_map<int, int> map;
//我在想怎么可以做到 规避掉 需要合并2个bucket的 这个步骤
//我尼玛,这不就是一个图论问题吗? 1个木桶中的木板相当于是节点,每个木桶是一个连通分量
//那,首先不得邻接表建图啊
//然后每个连通分量最为一个 vector 放到vec_set中不可以吗
//一个时间复杂度可能比较高 算法是 利用bfs广搜思想,找到1个连通分量的 所有 节点
int cnt_bucket = 0;
for (int i = 1; i <= n; i++)
{
//如果是爷爷没有提及到的 元素,直接continue
if (map.count(i) != 0 || num_set.count(i) == 0)
{
//已经访问过这个节点了 或者 爷爷没有提及到这个元素
continue;
}
else
{
//没有访问过,需要bfs,搜索得到一个新的 连通分量
queue<int> q;
//<0>初值
q.push(i);
vector<int> new_bucket;
while (!q.empty())
{
int first = q.front();
q.pop();//取出队首元素 , 然后记得出队
//然后开始加入到map中,并且取出邻接表中的 && 没有在map中count的元素入队
map[first] = cnt_bucket;
new_bucket.push_back(first);
//--入队
int size = graph[first].size(); //这里越界访问了吗,越界了,这里访问有问题
for (int j = 0; j < size; j++)
{
if (map.count(graph[first][j]) == 0)
{
//没有访问过,那就入队
q.push(graph[first][j]);
}
}
}
cnt_bucket++; //这时候才让bucket的数量++,计数是从1开始计数的,但是从0开始使用数组
vec_set.push_back(new_bucket);
}
}
//错误发生在以上代码中。。。
//--补充爷爷没有回忆到的那些元素,都 单独作为一个bucket
for (int i = 1; i <= n; i++)
{
if (map.count(i) == 0)
{
//创建一个 新的 一维数组
vector<int> new_bucket;
new_bucket.push_back(i);
vec_set.push_back(new_bucket);
//--又忘记更新map了
map[i] = cnt_bucket++;
}
}
//(2)将vec_set中的所有1维结果全部sort一遍好了
for (int i = 0; i < cnt_bucket; i++)
{
sort(vec_set[i].begin(), vec_set[i].end());
}
//调试1:构建的 vec_set二维数组是否有问题呢? --没问题,也就是最后一步逻辑有问题了
/*cout << "调试vec_set数组 :" << endl;
for (auto bucket : vec_set)
{
for (auto item : bucket)
{
cout << item << " ";
}
cout << endl;
}*/
//(3)最后一步,
unordered_set<int> cout_set; //输出1-n,反正
for (int i = 1; i <= n; i++)
{
//<1>如果已经输出过
if (cout_set.count(i))
{
continue;
}
//<2>否则找到i所在的 vec_set中的下标,然后全部从后往前输出
int index = map[i];
int size = vec_set[index].size();
for (int j = size - 1; j >= 0; j--)
{
//忘记用cout_set记录了
cout_set.insert(vec_set[index][j]);
cout << vec_set[index][j] << " ";
}
}
cout << endl;
}
return 0;
}
D.究极手:(给出一组节点 和 对应的度数 , 然后 构建一棵无向树)
解:
法一:
(我自己的 贪心算法)
代码如下:
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<algorithm>
#include<unordered_map>
#include<queue>
#include<unordered_set>
#include<utility>
#include<queue>
using namespace std;
bool compare(pair<int, int> a, pair<int, int> b)
{
return a.second > b.second;
}
int main()
{
//第一下反应 就知道这是一个图论的问题,
//思路:这不就是 给定不同节点的度数 , 然后构建一个无向连通无环简单图(构建一棵树)吗?妙
//数据结构:1个队列queue<pair<int,int>>,存放<1-n , remain_degree>,每次从队首开始
//利用一个vec[1-n]存放初始各个节点的degree ,初始让vec[1]make_pair入队
//往后,每次将队首节点的degree-1,并且加入 下一个节点,如果有degree变为0,出队 或者 不入队
//用一个二维数组ans存放边的结果
//最终如果queue为空--输出ans,否则输出-1
//-------------------------------------------------------
//先考虑一次:
int T;
cin >> T;
while (T--)
{
queue<pair<int, int>> q;
int n; cin >> n; //节点个数
vector<int> vec(n+1, 0);
vector<pair<int, int>> vec2(n + 1);
vector<pair<int,int>> ans; //节点对 的容器
for (int i = 1; i <= n; i++)
{
cin >> vec[i]; //输入节点的度数
vec2[i] = make_pair(i, vec[i]); //<节点下标, 度数>
}
//--对vec2进行sort , 利用自己写的 那个根据 度数降序排序的bool返回类型的 compare函数
sort(vec2.begin() + 1, vec2.end(), compare);
//--正式开始构建一个树:
//(1)第一个节点入队
q.push(vec2[1]);
//(2)将2-n号节点依次加入,不对,我觉得应该要让节点数目最多的节点先入队 , 有点贪心的感觉
//不管了,之后再去查资料看一下 到底如何构建一棵树,那就先让节点数最多的先入队
int flag = 1;//可以构建置1
for (int i = 2; i <= n; i++)
{
//先判断队列是否为空
if (q.empty())
{
cout << "-1"; // 说明无法构建
flag = 0;
break;
}
//--,取出队首元素,然后度数-1,新加入的节点的度数也-1
pair<int, int>& front_vex = q.front(); //传递引用
//--加入到ans中
ans.push_back(make_pair(front_vex.first, vec2[i].first));
front_vex.second--;
vec2[i].second--;
//--判断度数是否为0
if (front_vex.second == 0)
{
q.pop();
}
if (vec2[i].second != 0) //入队
{
q.push(vec2[i]);
}
}
//(3)最终结果判断
if (flag == 1 && q.empty())
{
int size_tmp = ans.size();
for (int i = 0; i < size_tmp; i++)
{
cout << ans[i].first << " " << ans[i].second << endl;
}
}
else if (flag == 1)
{
cout << -1 << endl;
}
}
return 0;
}
解释:
(1)我考虑的是“贪心”的思想!!! --对,就是贪心,我感觉证明了之后不会有 更优的构建方法了,哈哈哈哈哈
(2)vec[i]中存放的就是 第i个节点的 下标 和 度数 <index , degree>
(3)先排序,通过给 sort函数的 第三个参数 传递一个 compare函数 ,按照degree从大 到小排序
(4)利用一个队列q,
(5)先让 第一个degree最大的节点入队,然后每次 队首元素的度数减1,入队元素的 度数减1
(6)最后判断队列是否为空
法二:(官方解法,非常巧妙的 利用了 一个 “递归”思想)
代码:很简单,之后再写。。。
思想:
(1)
n==1 或者 2 : 显然
n>2时
总共的度数 为 2*n-2,那么,这n个节点中,根据鸽巢原理,一定至少有一个度数为1的节点,也一定至少有一个度数大于1的节点
因为 ,如果都>=2 ,2*n!=2*n-2 ; 如果都为1, n!= 2*n-2
(2)这时候,我们 这一对 1度数 和 >1度数连一条边,然后 n -> n-1个节点,2n-2度数 ->2*(n-2)-2度数,同样 这n-1个节点中,一定有 度数为1 和 大于1....妙!
E:Password (解一个二元不定方程)
解:
好吧,原来这种题目需要。。。找规律。。。我。。。
规律时 0,1,1,2,3,5,8......这个斐波那契数列的 奇数项带进去。。。
具体的证明如下:
代码如下:
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<algorithm>
#include<unordered_map>
#include<queue>
#include<unordered_set>
#include<utility>
#include<queue>
using namespace std;
int main()
{
int T;
cin >> T;
while (T--)
{
//从0-n之间找到对应的解带进去,
int n;
cin >> n;
//--
//干脆先生成一个从0开始的斐波那契数列 ,然后 取出 恰好小于n的奇数个作为ans
int f1 = 0, f2 = 1;
//f1作为 第奇数个斐波那契数, f2作为 第偶数个 斐波那契数
while (f2 <=n && f1 + f2<=n )
{
f1 = f1 + f2;
f2 = f1 + f2;
}
cout << f1 << endl;
}
return 0;
}
F:熊爷别笑了(本质上是 凸包问题)
解:
1.思路:
(1) “降维”的思路,将 这些半径相同的圆 全部 只考虑圆心的位置, 这种“降维”的思想是非常重要的
(2)这一题有点奥数的味道, 可以像参考题解那么想:用一根绳子,绕着所有的外围的 圆,这样得到的周长一定最短
也可以 直观去感觉, 你想吧,在外围绕一圈是不是 最后回到原点?是不是绕中心转过的角度是360度? 是不是 和 圆心平行的直线的路径最短, 否则两边之和大于 第三边
2.代码:
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<algorithm>
#include<unordered_map>
#include<queue>
#include<unordered_set>
#include<utility>
#include<queue>
#include<stack>
using namespace std;
//函数声明:
vector<vector<int>> outerTrees(vector<vector<int>> &trees); // 返回 一个二维数组,就是最终的凸包节点次序
int cross(const vector<int> & p, const vector<int> & q, const vector<int> & r);//计算 极角
double distance(const vector<int> & p, const vector<int> & q); //计算2点之间的距离
int main()
{
//只需要求一个 凸包,结果 就是凸包的 长度 + 一个圆周的长度即可
//处理输入:
int T;
cin >> T;
while (T--)
{
int n; // 有n个 玩偶
double R;
cin >> n >> R;
vector<vector<int>> trees(n + 1, vector<int>(2, 0));
for (int i = 1; i <= n; i++)
{
cin >> trees[i][0] >> trees[i][1]; //得到坐标点(x,y)
}
double total = 0;
total = total + acos(-1)*R * 2; //先加上 1个 圆周的周长
//(1)计算凸包的 调用 outerTreees函数,得到一个二维数组,其中存储了 凸包的节点次序的二维数组
vector<vector<int>> vec = outerTrees(trees);
int size = vec.size();
//(2)从头开始计算 这些点两两之间的 距离之和
total = total + distance(vec[0], vec[size - 1]);
for (int i = 1; i <= size - 1; i++)
{
total += (distance(vec[i - 1], vec[i]));
}
//(3)输出结果
cout << total << endl;
}
return 0;
}
vector<vector<int>> outerTrees(vector<vector<int>> &trees) {
int n = trees.size();
if (n < 4) {
return trees;
}
int bottom = 0;
/* 找到 y 最小的点 bottom*/
for (int i = 0; i < n; i++) {
if (trees[i][1] < trees[bottom][1]) {
bottom = i;
}
}
swap(trees[bottom], trees[0]);
/* 以 bottom 原点,按照极坐标的角度大小进行排序 */
sort(trees.begin() + 1, trees.end(), [&](const vector<int> & a, const vector<int> & b) {
int diff = cross(trees[0], a, b);
if (diff == 0) {
return distance(trees[0], a) < distance(trees[0], b);
}
else {
return diff > 0;
}
});
/* 对于凸包最后且在同一条直线的元素按照距离从大到小进行排序 */
int r = n - 1;
while (r >= 0 && cross(trees[0], trees[n - 1], trees[r]) == 0) {
r--;
}
for (int l = r + 1, h = n - 1; l < h; l++, h--) {
swap(trees[l], trees[h]);
}
stack<int> st;
st.emplace(0);
st.emplace(1);
for (int i = 2; i < n; i++) {
int top = st.top();
st.pop();
/* 如果当前元素与栈顶的两个元素构成的向量顺时针旋转,则弹出栈顶元素 */
while (!st.empty() && cross(trees[st.top()], trees[top], trees[i]) < 0) {
top = st.top();
st.pop();
}
st.emplace(top);
st.emplace(i);
}
vector<vector<int>> res;
while (!st.empty()) {
res.emplace_back(trees[st.top()]);
st.pop();
}
return res;
}
int cross(const vector<int> & p, const vector<int> & q, const vector<int> & r) {
return (q[0] - p[0]) * (r[1] - q[1]) - (q[1] - p[1]) * (r[0] - q[0]);
}
double distance(const vector<int> & p, const vector<int> & q) {
double tmp = (p[0] - q[0]) * (p[0] - q[0]) + (p[1] - q[1]) * (p[1] - q[1]);
tmp = sqrt(tmp);
return tmp;
}
H:Pause , Heal ,Explosion
解:
算了,之后的题目 找个人 比如邱伟林。锟谁的一起看比较好。。。那样比较有意思。。。
未完待续。。。