作者:指针不指南吗
专栏:蓝桥杯倒计时冲刺马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦
题目
链接: 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≤ 1 0 5 10^5 105
输入样例:
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<int> 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;i<n;i++) //我们顶点入队的顺序就是拓扑序列,直接输出就可以
printf("%d ",q[i]);
}
else
printf("-1"); //否则,该图没有拓扑序列,输出 -1
}
int main()
{
scanf("%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≤ 1 0 6 10^6 106 。
我的题解
#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.y<b.y; //从小到大
return a.x<b.x;
}
int main()
{
int 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<<num[m].x; //输出结果
return 0;
}
反思
第一次做的时候,想到了 set 和 map ,但是有忘记怎么写了,就直接看了题解
原来还可以使用结构体,确实简单了不少
sort函数的重载,这个还挺重要的,之前没有认真学习过,学完图论后,再系统地学习一个符号的重载