作者:指针不指南吗
专栏:蓝桥杯倒计时冲刺🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾
题目
链接: 848. 有向图的拓扑序列 - AcWing题库
给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y) , x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1。
数据范围
1≤n,m≤10510^5105
输入样例:
3 3 1 2 2 3 1 3
输出样例:
1 2 3
题里面的对拓扑图的定义,虽然很简洁,但我通过网上搜索资料,找到更加易懂的定义
一个有向图,如果图中有入度为 0 的点,就把这个点删掉,同时也删掉这个点所连的边。
一直进行上面出处理,如果所有点都能被删掉,则这个图可以进行拓扑排序。
如下图
图转自acwing
开始时,图是这样的状态,发现A的入度为 0,所以删除A和A上所连的边,结果如下图:
这时发现B的入度为 0,C的入度为 0,所以删除B和B上所连的边、C和C上所连的边,结果如下图:
这时发现发现D的入度为 0,所以删除D和D上所连的边(如果有就删),结果如下图:
这时整个图被删除干净,所有能进行拓扑排序。
思路
queue q;
q.push(入度=0);//把所有入度=0的点,放进队列里面
while(q.empty())
{t=对头枚举所有t的出边 t->j删掉 t->j 这个边,d[j]-- //入度--if(d[j]==0)q.push(j); //入队
}
题解
#include
using namespace std;const int N=1e5+10;int h[N],e[N*2],ne[N*2],idx;
int d[N]; //d表示入度
int q[N],tt=-1,hh=0;int n,m;void add(int a,int b) //定义一个边
{e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}void topsort() //拓扑排序函数
{for(int i=1;i<=n;i++) //遍历所有顶点,将入度=0的顶点,放进队列里面if(d[i]==0) q[++tt]=i; //使用 ++tt的好处是,最后tt就是我们想要的数,没有多加1;while(hh<=tt) //队列不空{int a=q[hh++]; //取出 队头for(int i=h[a];i!=-1;i=ne[i]) //删除以a为起点的边{int b=e[i]; //有一边是 a 指向 b;d[b]--; //删除a->b 的边,b的入度--if(d[b]==0) //入度=0,入队q[++tt]=b; }}if(tt==n-1) //如果所有顶点都进入过 队列,则说明 这个图 有拓扑序列{for(int i=0;iscanf("%d%d",&n,&m);memset(h,-1,sizeof h); while(m--){int a,b;scanf("%d%d",&a,&b);add(a,b); //模板 默写一遍d[b]++; //被指向的顶点的入度++}topsort(); //进行拓扑排序return 0;
}
补充
- 有向无环图才会有拓扑序列,所以有向无环图又称为拓扑图,无向图没有,环也没有
- 入度:有几条边进入某个顶点;出度:有几条边从某个顶点出去
题目
链接: 纸张尺寸 - 蓝桥云课 (lanqiao.cn)
在 ISO 国际标准中定义了 A0 纸张的大小为 1189mm ×× 841mm, 将 A0 纸 沿长边对折后为 A1 纸, 大小为 841mm ×× 594mm, 在对折的过程中长度直接取 下整 (实际裁剪时可能有损耗)。将 A1 纸沿长边对折后为 A2 纸, 依此类推。
输入纸张的名称, 请输出纸张的大小。
输入格式
输入一行包含一个字符串表示纸张的名称, 该名称一定是 A0、A1、A2、 A3、A4、A5、A6、A7、A8、A9 之一。
输出格式
输出两行,每行包含一个整数,依次表示长边和短边的长度。
样例输入1
A0
样例输出1
1189 841
样例输入 2
A1
样例输出 2
841 594
我的题解
#include
using namespace std;int l[10],w[10]; //用两个数组分别表示10种纸张的长、宽int main()
{int a=1189,b=841; //初始化l[0]=a,w[0]=b;for(int i=1;i<10;i++){l[i]=w[i-1]; //规律w[i]=l[i-1]/2;}char c;int x;scanf("%c%d",&c,&x); //用 c 吃掉 A,没啥用printf("%d\n%d",l[x],w[x]); //输出return 0;
}
反思
没有认真审题,以为每一次都是对半剪,还好调式了一遍
注意最后的输出格式,别最后好不容易算对了,结果格式错了,太冤了
2022年 C 组的题,还是很简单的
题目
链接: 数位排序 - 蓝桥云课 (lanqiao.cn)
小蓝对一个数的数位之和很感兴趣, 今天他要按照数位之和给数排序。当 两个数各个数位之和不同时, 将数位和较小的排在前面, 当数位之和相等时, 将数值小的排在前面。
例如, 2022 排在 409 前面, 因为 2022 的数位之和是 6, 小于 409 的数位 之和 13 。
又如, 6 排在 2022 前面, 因为它们的数位之和相同, 而 6 小于 2022 。
给定正整数 n*,m, 请问对 1 到 n 采用这种方法排序时, 排在第 m 个的元 素是多少?
输入格式
输入第一行包含一个正整数 n 。
第二行包含一个正整数 m 。
输出格式
输出一行包含一个整数, 表示答案。
样例输入
13 5
样例输出
3
样例说明
1 到 13 的排序为: 1,10,2,11,3,12,4,13,5,6,7,8,9。第 5 个数为 3 。
评测用例规模与约定
对于 30% 的评测用例, 1≤m≤n≤300 。
对于 50% 的评测用例, 1≤m≤n≤1000 。
对于所有评测用例, 1≤m≤n≤10610^6106 。
我的题解
#include
using namespace std;const int N=1e6+10;struct node //定义一个结构体,来放 每个数的原数和 各位和
{int x; //原数int y; //各位数和
}num[N]; int sum(int x) //求x各位数 的和
{int sum=0;while(x>0){sum+=x%10;x/=10;}return sum;
}bool cmp(node a,node b) //结构体,重载
{if(a.y!=b.y) return a.yint n,m;cin>>n>>m;for(int i=1;i<=n;i++) //初始化{num[i].x=i;num[i].y=sum(i);}sort(num+1,num+1+n,cmp); //进行排序 sort函数的重载cout<
反思
第一次做的时候,想到了 set 和 map ,但是有忘记怎么写了,就直接看了题解
原来还可以使用结构体,确实简单了不少
sort函数的重载,这个还挺重要的,之前没有认真学习过,学完图论后,再系统地学习一个符号的重载