蓝桥杯:染色时间https://www.lanqiao.cn/problems/2386/learning/?contest_id=80
问题描述
输入格式
输出格式
样例输入输出
样例输入
样例输出
评测用例规模与约定
解题思路:优先队列
AC代码(Java):
问题描述
小蓝有一个 n 行 m 列的白色棋盘, 棋盘的每一个方格都可以被染成彩色。
每个方格有一个染色时间, 不同方格的染色时间可能不同。如果一个方 格被触发了染色, 这个方格就会在 秒之后变成彩色, 然后将自己上下左右四 个方向相邻的方格触发染色。每个方格只能被触发染色一次, 第一次触发之后 的触发为无效触发。
给定每个方格的染色时间, 在时刻 0 触发第一行第一列的方格染色, 请问 多长时间后整个棋盘完成染色。
输入格式
输入的第一行包含两个整数 n,m, 分别表示棋盘的行数和列数。
接下来 n 行, 每行 m 个正整数, 相邻的整数之间用一个空格分隔, 表示每 个方格的染色时间。该部分的第 i 行第 j 个整数表示 , 即第 i 行第 j 列的方 格的染色时间。
输出格式
输出一行包含一个整数, 表示整个棋盘完成染色的时间。
样例输入输出
样例输入
2 3
1 2 3
4 5 6
样例输出
12
评测用例规模与约定
对于 30% 的评测用例, 1≤n,m≤10 。
对于 60% 的评测用例, 1≤n,m≤50 。
对于所有评测用例, 1≤n,m≤500,1≤≤1000 。
解题思路:优先队列
首先我们分析一下,题目要求我们从0,0处开始染色,也就是我们让0,0开始染色的时候,它的上下左右四个方向所有使用的时间为 自身染色时间+0,0处的染色时间,如:
将0,0处染色之后,图中染色时间分布情况:
0 3 3
4 5 6
我们知道,0,1处染色时间3肯定是要比1,0处染色时间快的,所以当0,1处染色完成,应该是这样的:
将0,1处染色之后,图中染色时间分布情况:
0 0 6
4 8 6
其实这里就可以看出来了,可以依次将染色的点存入队列之中,每次取出耗时最短的点,然后将其上下左右四个位置加上自身染色所需要的时间即可。
所以这道题可以用优先队列来做,我们定义一个类(结构体)Node,该类存放有三个属性,分别是x(行),y(列),time(染色累计耗时)。
static class Node{
int x;
int y;
int time; //耗时
public Node(int x,int y,int time){
this.x = x;
this.y = y;
this.time = time;
}
}
这样我们就可以将0,0处节点的信息存放到优先队列中,每次我们取出耗时最短的一个节点,y也就是先完成染色的节点先出来,然后将其染色累计耗时,添加到上下左右四个还没有染色的节点中(如果染色了,我们就将对应位置的节点的值设置为0,方便判断)。
依次再存入节点中,这样每次取出一个完成染色的节点,并且向四周开始没有染色的节点染色,这样优先队列中的最后一个节点就是我们完成染色所需要的时间。
AC代码(Java):
因为使用Scanner只通过了80%,但是这道题的思路应该就是优先队列不会错,所以我将输入流换成了StreamTokenizer,提高读取性能,才全部通过。
关于输入输出流性能,可以看:JAVA 输入输出优化
import java.io.*;
import java.util.*;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
static class Node{
int x;
int y;
int time; //耗时
public Node(int x,int y,int time){
this.x = x;
this.y = y;
this.time = time;
}
}
static int[][] dires = {{-1,0},{0,-1},{1,0},{0,1}}; //四个方向,上,右,下,左
//优化输入流
static StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public static int nextInt() throws IOException {
st.nextToken();
return (int) st.nval;
}
public static void main(String[] args) throws IOException{
//在此输入您的代码...
int n = nextInt();
int m = nextInt();
int[][] map = new int[n][m];
for(int i = 0;i<n;i++){
for(int j = 0;j<m;j++)
map[i][j] = nextInt();
}
//首先队列,每次耗时最短的节点先出队,最后出队的就是耗时最久的
PriorityQueue<Node> pq = new PriorityQueue<>((n1, n2)->(n1.time-n2.time)); //升序排序
pq.add( new Node(0,0,map[0][0]) );
//令该处为0,代表这个位置已经染色过了
map[0][0] = 0;
int ans = 0;
while(!pq.isEmpty()){
Node node = pq.poll();
//将node节点上下左右的节点入队,并修改自己为 0,代表该节点已经染色
for(int[] dire : dires){
int x = node.x+dire[0];
int y = node.y+dire[1];
if(x>=0 && x<n && y>=0 && y<m && map[x][y]!=0){
pq.add(new Node(x,y,map[x][y]+node.time));
//令该处为0,代表这个位置已经染色过了
map[x][y] = 0;
}
}
//该位置已经染色
map[node.x][node.y] = 0;
ans = node.time;
}
System.out.println(ans);
}
}