题目
问题描述
小明国庆节准备去某星系进行星际旅行,这个星系里一共有 n 个星球,其中布置了 m 道双向传送门,第 i 道传送门可以连接 ai,bi 两颗星球(ai≠bi 且任意两颗星球之间最多只有一个传送门)。
他看中了一款 “旅游盲盒”,一共有 Q 个盲盒,第 i 个盲盒里的旅行方案规定了旅行的起始星球 xi 和最多可以使用传送门的次数 yi。只要从起始星球出发,使用传送门不超过规定次数能到达的所有星球都可以去旅行。
小明关心在每个方案中有多少个星球可以旅行到。小明只能在这些盲盒里随机选一个购买,他想知道能旅行到的不同星球的数量的期望是多少。
输入格式
输入共 m+Q+1 行。
第一行为三个正整数 n,m,Q。
后面 m 行,每行两个正整数 ai,bi。
后面 Q 行,每行两个整数 xi,yi。
输出格式
输出共一行,一个浮点数(四舍五入保留两位小数)。
样例输入
3 2 3
1 2
2 3
2 1
2 0
1 1
样例输出
2.00
样例说明
第一个盲盒可以旅行到 1,2,3。
第二个盲盒可以旅行到 2。
第三个盲盒可以旅行到 1,2。
所以期望是 (3+1+2)/3=2.00。
思路
先来表述一下题意,就是一共有n个星球,然后这n个星球中设置了m道双向传送门,第i道传送门可以连接星球ai,bi。然后小明看中了一款旅游盲盒,一共有Q个,每个盲盒里边存的是旅行方案,这个旅行方案规定了旅行的起始星球xi和最多可以使用传送门的次数yi。然后小明只能选择其中一个盲盒,然你求一下能旅行到不同星球的数量的期望是多少。
然后这题我觉得还是比较友好,给了你一个样例说明,那我们先以这个样例为例,讲解一下为什么是这个结果。
上边的图是依照题意和样例画出来的图,因为一共有m行(a,b),就是你可以把(a,b)就看成一条边,然后(1,2),(2,3),就正好是图上的两条边。
然后再来看样例中一共有Q行的(x,y),首先(2,1)就是这一个盲盒,你需要从星球2为起点,然后至多使用1次传送门,那我们现在假设我们在图中的2号节点上,因为至多使用1次传送门嘛,所以可以使用0次或者1次,那么使用0次的情况下,我们只能到达星球2;使用1次的情况下,我们就可以从星球2到达星球1,也可以从星球2到达星球3,所以使用0次、使用1次这两种情况下,从星球2可以到达星球2、星球1、星球3,所以能到达3个星球。
再来看(2,0),就是还是从星球2出发,但是至多只能使用一次传送门,这种情况下,我们只能使用0次,所以我们那也去不了,只能到达星球2,所以只能到达1个星球。
最后一行(1,1),就表示从星球1为起点,然后你只多使用一次传送门,当你使用0次传送门的时候,你只能到达星球1;当你使用1次传送门的时候,你只能到达星球2,综上,你可以到达星球1、星球2这2个星球。
所以这3个盲盒,总共可以到达的星球数为3+1+2 = 6,然后除以星球数Q=3得到期望,6/3=2。注意保留两位小数。
那么下边就是怎么求解的过程。
我感觉题意应该都能理解,主要就是不知道怎么去写代码,或者不知道怎么去利用板子,反正我到写代码这一步就总是完蛋,我觉得可能是我题目见的比较少,一方面量没达到,另一方面确实脑子不太灵光,一时半会不会举一反三T_T。
我是在蓝桥云课上做的这题,上边给我的标签是Dijkstra,我:???。我完全不知道怎么去用这个算法。把读入样例的代码写完之后,是一点也写不出来了。然后看了大佬们的题解,发现好多用Floyd,我就在数据结构课上知道他是个求多源最短路径的算法,时间复杂度好像还有点高,是O(n^3)好像,我发现自己也无法把Floyd与这道题目联系起来。
但是看大佬题解的时候,其实代码还是挺好理解的,因为之前有知道Floyd的算法知识,所以上来就直接找了那三层for循环,发现并没有什么奇特的地方。也就说明这道题的难点不在于你怎么用Floyd,而在于你怎么去对每个盲盒能到达的星球数进行计数。
那么如何对能到达的星球数进行计数?就是通过上边那幅图的讲解,应该可以想到,如果起点到任意一点b的距离<=传送门的次数,就说明这个盲盒可以到达b星球,所以我们只需要求出整张图里边各个点的到其他点的最短路径即可,然后通过循环判断起始点到其他点的距离就行。
那么这个距离我们怎么衡量?我们可以把边上的权值全都设为1,这样你再看上边的图,从星球1到星球2的距离就是1,也就是说,需要一次传送门。加入从星球1到星球3,距离就是2,需要两个传送门,就正好能够利用到传送门数这个变量,去求得最终能到达的星球总数。
然后看下边的代码,Floyd就是那三重for循环,最外层是中转节点,第二层是起始节点,最内层是终止节点。然后对星球计数的部分就是sum++所在的双层for循环。那个g[][]数组表示的是两个星球间是否有边,右边我们就默认权值为1,然后x[]就是第i个盲盒的起始星球是谁,y[]就是第i个盲盒最多可以使用的传送门次数。这样的话,sum++所在的那两层for循环就比较好理解啦,最外层循环遍历Q个盲盒,那么x[i]也就是第i个盲盒的起始星球Xi,那么y[i]也就是至多可以使用传送门的次数;然后内层循环表示可以到达的星球,也就是边的终点(不管实际能不能到达,反正都在那个g[][]二维数组里边),,然后我们判断g[x[i]][j]是否<=y[i],就是从星球Xi到星球j的距离是否<=可以使用的传送门次数,如果小于等于,可到达的星球总数sum就加1;否则就不加。然后除Q,就可以得到最终的期望。
代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int Q = sc.nextInt();
int [][]g = new int[n + 1][n + 1];
for(int i = 1;i <= n;i ++) {
Arrays.fill(g[i],100000);
g[i][i] = 0;
}
for(int i = 1;i <= m;i ++) {
int a = sc.nextInt();
int b = sc.nextInt();
g[a][b] = 1;
g[b][a] = 1;
}
int x[] = new int[Q + 1];
int y[] = new int[Q + 1];
for(int i = 1;i <= Q;i ++) {
x[i] = sc.nextInt();
y[i] = sc.nextInt();
}
for(int k = 1;k <= n;k ++) {
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= n;j ++) {
g[i][j] = Math.min(g[i][k] + g[k][j],g[i][j]);
}
}
}
double sum = 0;
for(int i = 1;i <= Q;i ++) {
for(int j = 1;j <= n;j ++) {
if (g[x[i]][j] <= y[i]) {
sum ++;
}
}
}
double ans = sum / Q;
System.out.printf("%.2f",ans);
}
}