PTA团体程序设计天梯赛

  • 数据结构类型
    • L3-002 特殊堆栈(树状数组)
    • L3-003 社交集群(并查集)
  • 搜索
    • L3-004 肿瘤诊断(三维bfs)
      • 确保bfs只遍历一次的方法
  • 图论
    • L3-005 垃圾箱分布(多次SPFA)
    • L3-007 天梯地图 (最短路+输出指定路径)

数据结构类型

L3-002 特殊堆栈(树状数组)

题目链接

题目大意
本题的难点是维护一个动态的中值。

解题思路
因为值可能是不按大小顺序给出的,因此我们无法利用优先队列来维护,原因是在进行pop的时候可能弹出的是下边或者中间的值,而不是优先队列顶部的值。对于中值,我们对于每一个值如果出现一次,那么其次数加1,那么中值就转变为了这个次数序列中出现次数的中值(因为这个序列是单调的),那么可以单点修改与单点查询的数据结构,就是树状数组了。

  • 对于求中值,我们可以在0 ~ N 中进行二分。

代码:

#include<iostream>
#include<string>
#include<stack>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int t[N];
stack<int>st;
int n ,x;int lowbit(int x){ return x & -x; }void add(int k , int v){for( ; k < N ; k += lowbit(k)) t[k] += v;
}int get(int n){int ans = 0;for( ; n ; n -= lowbit(n))ans += t[n];return ans;
}int PeekMedian(){int l = 1 , k = (st.size() + 1)/2 , r = N - 1;while(l < r){int mid = (l + r)>>1;if(get(mid) >= k) r = mid;else l = mid + 1;}return l;
}int main(){cin>>n;while(n--){string s;cin>>s;if(s == "Pop"){if(st.size() == 0)cout<<"Invalid\n";else{x = st.top();cout<<st.top()<<endl;st.pop() , add(x , -1);}}else if(s == "Push"){cin>>x;st.push(x) ,add(x,1);}else{if(st.size() == 0)cout<<"Invalid\n";else cout<<PeekMedian()<<endl;}}return 0;
}

L3-003 社交集群(并查集)

题目链接

题目大意
题目是给出了每一个人的兴趣的编号的集合,对于存在一个兴趣相同的人我们认为其在一个圈子中。然后问你有多少兴趣圈,与每一个圈子有多少人。

解题思路
对于每一个人我们存下,其兴趣圈中的一个代表元素(不妨是第一个元素),之后将其兴趣圈中的所有值进行合并。对于没一个人都这样操作以后。我们枚举这n个人,我们

代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1100;int p[N];
char ch;
int n , f[N];
int pos[N] ,cnt , p[N];bool cmp(int a, int b){return a > b;}void  init(){for(int i = 1 ;i  < N ; ++i) f[i] = i;
}int find(int x){if(x == f[x])return x;else return f[x] = find(f[x]);
}void join(int x,int y){x = find(x) , y = find(y);if(x != y)f[x] = y;
}int main(){cin>>n;init();for(int i = 1 ; i <= n ; ++i){int k , y ;cin>>k>>ch;for(int j = 1 ;j <= k ; ++j){int x;cin>>x;if(j == 1)y = x , p[i] = y;else join(x ,y);}}for(int i = 1; i <= n ; ++i){int x = find(p[i]);pos[x] ++;}sort(pos ,pos + N , cmp);for(int i = 0 ; pos[i] ; ++i)cnt++;cout<<cnt<<endl;for(int i = 0 ; i < cnt ; ++i){if(i)cout<<" ";cout<<pos[i];}return 0;
}

搜索

L3-004 肿瘤诊断(三维bfs)

题目链接

解题思路
这可以说是一个三维bfs的板子题,这里x,y,z的顺序不重要只要你清楚就可以了。一般对于二维,三维的移动我们都会建立一个坐标变化的数组。我们在进行bfs的时候要确保每一个点只遍历一次。这里我们有两种方式确保只遍历一次。

确保bfs只遍历一次的方法

  1. 我们在每一次出队的时候将出队的元素置为1,为了确保遍历一次我们在置为1的上边先判断一下,如果该元素已经是1了就跳过。这样做可行的原因是,即使前后两个节点指向的节点又交集,但是由于队列的先进先出,如果我们在置为1之前跳过已经是1的点,也就是对于交集虽然都在队列中不过我们只会遍历先进去的节点,这样就确保了正确性。
    代码:
void bfs(int z ,int x ,int y){queue<node>q;q.push({z,x,y});while(q.size()){node u = q.front();q.pop();if(st[u.z][u.x][u.y])continue;s ++ ;     st[u.z][u.x][u.y] = 1;for(int i = 0 ;i < 6 ; ++i){int zz = u.z + dz[i] , xx = u.x + dx[i] , yy = u.y + dy[i];if(zz >= 0 && zz < L && xx >= 0 && xx < n  && yy >= 0 && yy < m && !st[zz][xx][yy] && str[zz][xx][yy] == 1)q.push({zz,xx,yy});}}
}
  1. 第二种是在将满足条件的节点压入队列的时候就置为1,这样是确保了在队列元素中的唯一性,显然能满足节点只遍历一次的情况。

代码:

void bfs(int z ,int x ,int y){queue<node>q;q.push({z,x,y});st[z][x][y] = 1;while(q.size()){node u = q.front();q.pop();s ++ ;     for(int i = 0 ;i < 6 ; ++i){int zz = u.z + dz[i] , xx = u.x + dx[i] , yy = u.y + dy[i];if(zz >= 0 && zz < L && xx >= 0 && xx < n  && yy >= 0 && yy < m && !st[zz][xx][yy] && str[zz][xx][yy] == 1){q.push({zz,xx,yy});st[zz][xx][yy] = 1; // 在这里才不会重复加入}}}
}

代码:

#include <iostream>
#include <string>
#include <cstring>
#include<queue>
using namespace std;const int N = 1300 , M = 130;
int n, m, L, t;
int str[M][N][M];
bool st[M][N][M];int s, ans, dx[6] = {0, 0, 1, -1,0,0}, dy[6] = {1, -1, 0, 0, 0, 0},dz[6] = {0,0,0,0,1,-1};struct node{int z,x,y;
};void bfs(int z ,int x ,int y){queue<node>q;q.push({z,x,y});//  st[z][x][y] = 1;while(q.size()){node u = q.front();q.pop();if(st[u.z][u.x][u.y])continue;s ++ ;     st[u.z][u.x][u.y] = 1;for(int i = 0 ;i < 6 ; ++i){int zz = u.z + dz[i] , xx = u.x + dx[i] , yy = u.y + dy[i];if(zz >= 0 && zz < L && xx >= 0 && xx < n  && yy >= 0 && yy < m && !st[zz][xx][yy] && str[zz][xx][yy] == 1){q.push({zz,xx,yy});//  st[zz][xx][yy] = 1; // 在这里才不会重复加入}}}
}void solve() {memset(st, 0, sizeof st);for(int k = 0 ; k < L ; ++k)for (int i = 0 ; i < n ; ++i)for (int j = 0 ; j < m ; ++j)if (str[k][i][j] == 1 && !st[k][i][j]) {s = 0;bfs(k , i , j);if ( s >= t)ans += s;}
}int main() {cin >> n >> m >> L >> t;for(int k = 0 ; k < L ; ++k) for (int i = 0 ; i < n ; ++i)for (int j = 0 ; j < m ; ++j)cin>>str[k][i][j];solve();cout << ans << endl;return 0;}

图论

L3-005 垃圾箱分布(多次SPFA)

题目链接

解题思路
对于这里首先我们要求的是垃圾箱到各个居名点的最短距离,这里有一个小技巧就是将垃圾箱也进行编号是 n + c ,c 是垃圾箱的编号,然后一起建边。之后每一次都以垃圾箱为起点进行spfa,大致步骤如下

分析:
这道题对每个垃圾点进行spfa即可,然后找到符合以下条件的垃圾点:

居民点与垃圾箱之间的最短距离不超过Dist
垃圾箱到居民点的最短距离最长
若符合2的不唯一,则选择平均距离最短的
若符合3的不唯一,则选择编号最小的

  • 对于对某一位数进行四舍五入的办法是,先将该数扩大倍数,使得要四舍五入的位数变成小数点的第一位,四舍五入之后再缩小相同倍数。

代码:

#include<iostream>
#include<queue>
#include<string>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;const int N = 1e4 + 100 , M = 1e5 + 10;int n , m , L,t;
int h[N] ,e[M] , ne[M] ,w[M] ,cnt;
bool st[N];
int d[N];struct rub{int id  ;double mix ,avg;bool operator < (rub w){if(mix == w.mix && avg == w.avg)return id < w.id;else if(mix == w.mix)return avg < w.avg;else return mix > w.mix;}
}a[N];void add(int u , int v ,int val){e[++cnt] = v , ne[cnt] = h[u] , w[cnt] = val , h[u] = cnt;
}void spfa(int s){memset(st, 0 ,sizeof st);memset(d , 0x3f , sizeof d);queue<int>q;q.push(s);d[s] = 0;while(q.size()){int u = q.front();q.pop();st[u] = false;for(int i = h[u]; ~i ; i = ne[i]){int v = e[i];if(d[v] > d[u] + w[i]){d[v] = d[u] + w[i];if(!st[v]) q.push(v) , st[v] = true;}}}
}int main(){memset(h,-1,sizeof h);cin>>n>>m>>L>>t;while(L--){string a ,b;int d , x,y;cin>>a>>b>>d;if(a[0] == 'G') x = n + atoi(a.substr(1).c_str());else x = atoi(a.c_str());if(b[0] == 'G') y = n + atoi(b.substr(1).c_str());else y = atoi(b.c_str());add(x,y,d) ,add(y,x,d);}int tot = 0 , mx ;for(int i = n + 1 ; i <= n + m ; ++i ){spfa(i);double sum = 0.0;mx = 0;a[tot] = {i - n ,1e10 , 0};for(int j = 1 ; j <= n ; ++j) a[tot].mix = min(a[tot].mix , d[j] + 0.0) , mx = max(mx , d[j]) , sum += d[j];if(mx > t)continue; // 有超过最大距离限制的就跳过a[tot++].avg = sum * 1.0 / (n + 0.0);}sort(a , a + tot); if(!tot)cout<<"No Solution\n";else {printf("G%c\n",a[0].id + '0');printf("%.1lf %.1lf\n",a[0].mix , round(a[0].avg * 10.0) / 10.0 );}return 0;
}

L3-007 天梯地图 (最短路+输出指定路径)

题目:题目链接
内容:

本题要求你实现一个天梯赛专属在线地图,队员输入自己学校所在地和赛场地点后,该地图应该推荐两条路线:一条是最快到达路线;一条是最短距离的路线。题目保证对任意的查询请求,地图上都至少存在一条可达路线。

输入格式:

输入在第一行给出两个正整数N(2 ≤ N ≤ 500)和M,分别为地图中所有标记地点的个数和连接地点的道路条数。随后M行,每行按如下格式给出一条道路的信息:

V1 V2 one-way length time

其中V1和V2是道路的两个端点的编号(从0到N-1);如果该道路是从V1到V2的单行线,则one-way为1,否则为0;length是道路的长度;time是通过该路所需要的时间。最后给出一对起点和终点的编号。

输出格式:

首先按下列格式输出最快到达的时间T和用节点编号表示的路线:
Time = T: 起点 => 节点1 => … => 终点

然后在下一行按下列格式输出最短距离D和用节点编号表示的路线:
Distance = D: 起点 => 节点1 => … => 终点

如果最快到达路线不唯一,则输出几条最快路线中最短的那条,题目保证这条路线是唯一的。而如果最短距离的路线不唯一,则输出途径节点数最少的那条,题目保证这条路线是唯一的。
如果这两条路线是完全一样的,则按下列格式输出:
Time = T; Distance = D: 起点 => 节点1 => … => 终点

解题思路
思路就是分别以time,length为权值跑两边Dijkstra,记住要用上堆优化。
我们用一个数组存路径,这个数组p[v]的含义是 指向 v 节点的是p[v]。同时为了找到正确的路径,我们还需要nc[v]表示到达v 节点走过的最小节点数量 , 与 fd[v]表示走到v的最短距离。

  • 测试点2的意思就是:最快的最短是距离最短而不是节点最少!
    所以说虽然你其他的测试点都过了,只是数据恰好距离最短和节点最少等效而已,你的代码还是存在问题的!
  • 对于检测路径是否一样,我们可以利用迭代来进行,从终点依次向前推,如果遇到不同的节点就返回false , 否则只到起点都是一样的那么就返回true。

代码:

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;typedef pair<int ,int>PII;
const int N = 5100 , M = 5e5 + 10;int n , m , v1 ,v2;
int h[N] , e[M] , ne[M] ,w1[M] ,w2[M] ,cnt;
bool st[N];
int dd[N] ,dt[N] ,pd[N],pt[N] ,nc[N] , fd[N];void add(int u , int v ,int val1,int val2){e[++cnt] = v , ne[cnt] = h[u] , w1[cnt] = val1 , w2[cnt] = val2 , h[u] = cnt;
}void dijstra1(int s){memset(dd , 0x3f ,sizeof dd);dd[s]  =  0 ;priority_queue<PII>heap;heap.push({0 , s});st[s] = true;while(heap.size()){int u = heap.top().second;heap.pop();for(int i = h[u] ;~i ; i = ne[i]){int v = e[i];if(dd[v] >= dd[u] + w2[i]){if(dd[v] > dd[u] + w2[i]){dd[v] = dd[u] + w2[i] , pd[v] = u , fd[v] = fd[u] + w1[i];if(!st[v]) heap.push({-dd[v] ,v}) , st[v] = true;}else if(fd[v] > fd[u] + w1[i]){pd[v] = u ,fd[v] = fd[u] + w1[i];}}}}
}void dijstra2(int s){memset(dt , 0x3f ,sizeof dt);dt[s] = 0;priority_queue<PII>heap;heap.push({0 , s});st[s] = true;while(heap.size()){int u = heap.top().second;heap.pop();for(int i = h[u] ;~i ; i = ne[i]){int v = e[i];if(dt[v] >= dt[u] + w1[i]){if(dt[v] > dt[u] + w1[i]){dt[v] = dt[u] + w1[i] , pt[v] = u ,nc[v] = nc[u] + 1;if(!st[v]) heap.push({-dt[v] ,v}) , st[v] = true;}else if(nc[v] > nc[u] + 1){pt[v] = u , nc[v] = nc[u] + 1;}}}}
}bool check(){int ed = v2 ;while(ed != v1){if(pd[ed] != pt[ed])return false;ed = pd[ed];}return true;
}void dfs(int u , int p[]){if(u == v1){cout<<v1;return ;}dfs(p[u] , p);cout<<" => "<<u;
}int main(){memset(h , -1 ,sizeof h);cin>>n>>m;while(m--){int a,b,op , c,d;cin>>a>>b>>op>>c>>d;add(a,b ,c,d);if(!op)add(b,a,c,d);}cin>>v1>>v2;dijstra1(v1);memset(st , 0 ,sizeof st);dijstra2(v1);if(check()){printf("Time = %d; Distance = %d: ",dd[v2] ,dt[v2]);dfs(v2 ,pd);}else{printf("Time = %d: ",dd[v2]);dfs(v2 ,pd);puts("");printf("Distance = %d: ",dt[v2]);dfs(v2 ,pt);}return 0;
}

PTA团体程序设计天梯赛篇(五)---- 难题篇一(30分题目)相关推荐

  1. PTA 团体程序设计天梯赛-练习集 L1-034 点赞(20 分)C语言

    L1-034 点赞(20 分) 微博上有个"点赞"功能,你可以为你喜欢的博文点个赞表示支持.每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性.本题就要求 ...

  2. PTA团体程序设计天梯赛-练习集(3)

    PTA团体程序设计天梯赛-练习集 L1-001 Hello World (5 分) 这道超级简单的题目没有任何输入. 你只需要在一行中输出著名短句"Hello World!"就可以 ...

  3. PTA|团体程序设计天梯赛-练习题目题解锦集(C/C++)(持续更新中……)

    PTA|团体程序设计天梯赛-练习题目题解锦集(持续更新中) 实现语言:C/C++:      欢迎各位看官交流讨论.指导题解错误:或者分享更快的方法!! 题目链接:https://pintia.cn/ ...

  4. PTA团体程序设计天梯赛-练习集

    PTA团体程序设计天梯赛-练习集 L1-024 后天 L1-025 正整数A+B L1-026 I Love GPLT L1-027 出租 L1-029 是不是太胖了 L1-030 一帮一 L1-03 ...

  5. PTA团体程序设计天梯赛-练习集Level-1(参考代码C语言/Python版)

    本题目集截止到2022年天梯赛 受个人水平限制,<PTA团体程序设计天梯赛-练习集>中暂时只能把Level-1的题目做出来(也许有些Level-2的题可以写出来?)-我不是专门搞竞赛的,参 ...

  6. PTA团体程序设计天梯赛(L1-031~L1-040)

    PTA团体程序设计天梯赛[L1-031~L1-040] L1-031 到底是不是太胖了 (10 分) L1-032 Left-pad (20 分) L1-033 出生年 (15 分) L1-034 点 ...

  7. PTA团体程序设计天梯赛(L1-061~L1-070)

    PTA团体程序设计天梯赛[L1-061~L1-070] L1-060 心理阴影面积 (5 分) L1-062 幸运彩票 (15 分) L1-063 吃鱼还是吃肉 (10 分) L1-064 估值一亿的 ...

  8. PTA|团体程序设计天梯赛-练习题库集

    文章目录 关于爬取脚本的编写 L1-001 Hello World! (5 分) L1-002 打印沙漏 (15 分) L1-003 个位数统计 (15 分) L1-004 计算摄氏温度 (5 分) ...

  9. PTA团体程序设计天梯赛篇(一)----模拟专题

    模拟专题 字符串模拟 c++ 常用字符串处理函数 1. 截取子串 2. 替换子串 3. 查找子串 4.删除字符串 5.判断与转换函数 6 翻了(替换子串) 敲笨钟(字符串查找+字符串替换) 估值一亿的 ...

最新文章

  1. 特斯联再获20亿元融资,跻身AIoT独角兽,光大京东讯飞万达入股
  2. java程序课程总结_java课程总结
  3. 选项卡TabPanel控件
  4. NYOJ 127 星际之门(一)
  5. JAVA泛型编程笔记
  6. SpringAMQP--入门案例的消息发送
  7. oracle基本的操作命令,oracle命令基本操作
  8. 以某种结构遍历添加的基础类
  9. Android 发送HTTP GET POST 请求以及通过 MultipartEntityBuilder 上传文件
  10. PostgreSQL的 initdb 源代码分析之十
  11. 科研生活:避免碌碌无为的感觉
  12. 哪种linux好,哪种LINUX好用
  13. 清华大学最新科研进展汇总(2020-2021年)
  14. 求1-100的和的几种方法
  15. 1条命令解决不能完成此操作,因为项目“Karabiner-Elements”已被锁定
  16. 修改linux下用户和root密码
  17. 传统支付方式不能满足线下支付的需求
  18. 一图理解geos的九交矩阵模型——面面关系
  19. 实验吧-密码学(三)
  20. python11——模块与包

热门文章

  1. socket通信流程图
  2. 电脑物理内存与虚拟内存的区别与关系
  3. ubuntu vi 按方向键出现很多字母是怎么回事?(没安装vim)
  4. web前端入门学习 css(4)(盒子模型)
  5. 【哲学】自由意志是什么?(主观能动性)
  6. 网关是个啥?为什么电脑不设置网关就没法上网?笔记本为啥不用设置网关?
  7. 复习java的java.io.File类,深入理解并熟练使用
  8. 关于 MySQL5.7.log 版本导出 SQL 语句再导入 8.0.13 版本出现 Incorrect datetime value: ‘0000-00-00 00:00:00‘ 错误的解决办法
  9. wordpress留言板comments.php添加自定义字段,php – 如何在WordPress / WooCommerce 3中的注释表单中添加自定义字段...
  10. java使用stream将List转为Map