2020年第十一届蓝桥杯决赛JAVA B H题"画廊"
2020国赛 JAVA B组 个人题解目录
试题 H: 画廊
时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
小蓝办了一个画展,在一个画廊左右两边陈列了他自己的作品。为了使画
展更有意思,小蓝没有等距陈列自己的作品,而是按照更有艺术感的方式陈列。
在画廊的左边陈列了 L 幅作品,在画廊的右边陈列了 R 幅作品,左边的作品距
离画廊的起点依次为 u 1 , u 2 , ···, u L ,右边的作品距离画廊起点依次为 v 1 , v 2 , ···,
v R 。
每周,小蓝要整理一遍自己的每一幅作品。整理一幅作品的时间是固定的,
但是要带着沉重的工具。从一幅作品到另一幅作品之间的距离为直线段的长度。
小蓝从画廊的起点的正中央(左右两边的中点)出发,整理好每一幅画,
最终到达画廊的终点的正中央。已知画廊的宽为 w。
请问小蓝最少带着工具走多长的距离?
【输入格式】
输入的第一行包含四个整数 L, R, d, w,表示画廊左边和右边的作品数量,
以及画廊的长度和宽度。
第二行包含 L 个正整数 u 1 , u 2 , ···, u L ,表示画廊左边的作品的位置。
第三行包含 R 个正整数 v 1 , v 2 , ···, v R ,表示画廊右边的作品的位置。
【输出格式】
输出一个实数,四舍五入保留两位小数,表示小蓝最少带着工具走的距离。
【样例输入】
3 3 10 2
1 3 8
2 4 6
【样例输出】
14.71
【样例说明】
小蓝从起点开始,首先到达左边第一幅作品(走动距离
√ 2),然后到达左
边第二幅作品(走动距离 2),然后到达右边第一幅作品(走动距离
√ 5),然后
到达右边第二幅和第三幅作品(走动距离 2 和 2),然后到达左边第三幅作品
(走动距离 2
√ 2),最后到达画廊终点(走动距离 √ 5)。
总共距离为
√ 2 + 2 + √ 5 + 2 + 2 + 2 √ 2 + √ 5 ≈ 14.71。
【评测用例规模与约定】
对于 40% 的评测用例,1 ≤ L,R ≤ 10, 1 ≤ d ≤ 100, 1 ≤ w ≤ 100。
对于 70% 的评测用例,1 ≤ L,R ≤ 100, 1 ≤ d ≤ 1000, 1 ≤ w ≤ 1000。
对于所有评测用例,1 ≤ L,R ≤ 500, 1 ≤ d ≤ 100000, 1 ≤ w ≤ 100000,
0 ≤ u 1 < u 2 < ··· < u L ≤ d, 0 ≤ v 1 < v 2 < ··· < v R ≤ d。
解析:动态规划
思路:dp[i][j][k] 其中表示当前左边从下往上整理到了第i幅画,j表示当前右边当前整理到了第j幅画,k表示人当前在左边还是右边,0表示左边,1表示右边,dp[i][j][k]表示整理了左边前i幅画和右边前j幅画后人在左/右时需要走的最小距离。
(1)初始化:
dp[1][0][0]为起点到左边第一幅画的距离;
dp[0][1][1]为起点到右边第一幅画的距离;
dp[i][0][0] = dp[i-1][0][0] + 左边i-1到i的距离;
dp[0][j][1] = dp[0][j-1][1] + 右边j-1到j的距离;
(2)递推:
中间的每一步只会来自同一边的上一副画和对边的某幅画即:
dp[i][j][0] = min(dp[i-1][j][0]+左边i-1到i的距离 , dp[i-1][j][1]+右边j到左边i的距离);
dp[i][j][1] = min(dp[i][j-1][1]+右边j-1到j的距离 , dp[i][j-1][0]+左边i到右边j的距离);
(3)结论:
最终结果=min(dp[max i ][max j][0]+左边最后一副画到终点的距离,dp[max i ][max j][1]+右边最后一副画到终点的距离)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class H {
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));//数据量很大,需要高效输
in.nextToken();
int L = (int)in.nval;
in.nextToken();
int R = (int)in.nval;
in.nextToken();
double d = (double)in.nval;
in.nextToken();
double w = (double)in.nval;
double sx = w/2 , sy = 0 , ex = w/2 , ey = d;//起点终点坐标
double[][][] dp = new double[L+1][R+1][2];
point[] Lp = new point[L+1];
point[] Rp = new point[R+1];
for(int i=1;i<=L;i++) {
in.nextToken();
int y = (int)in.nval;
Lp[i] = new point(0, y);
}
Lp[0] = new point(-99999999, -99999999) ;
for(int i=1;i<=R;i++) {
in.nextToken();
int y = (int)in.nval;
Rp[i] = new point(w, y);
}
Rp[0] = new point(-99999999, -99999999) ;
for(int i=1;i<=L;i++)
for(int j=1;j<=R;j++)
for(int k=0;k<=1;k++) dp[i][j][k]=99999999;
dp[1][0][0] = cal(Lp[1].x,Lp[1].y,sx,sy);
dp[0][1][1] = cal(Rp[1].x,Rp[1].y,sx,sy);
dp[1][1][0] = dp[0][1][1] + cal(Lp[1].x, Lp[1].y, Rp[1].x,Rp[1].y);
dp[1][1][1] = dp[1][0][0] + cal(Lp[1].x, Lp[1].y, Rp[1].x,Rp[1].y);
for(int i=2;i<=L;i++) dp[i][0][0] = dp[i-1][0][0]+Lp[i].y-Lp[i-1].y;
for(int i=2;i<=R;i++) dp[0][i][1] = dp[0][i-1][1]+Rp[i].y-Rp[i-1].y;
for(int i=1;i<=L;i++) {
for(int j=1;j<=R;j++) {
for(int k=1;k>=0;k--) {
if(i==1&&j==1)break;
if(k==0)dp[i][j][0] = Math.min(dp[i-1][j][0]+Lp[i].y-Lp[i-1].y, dp[i-1][j][1]+cal(Lp[i].x, Lp[i].y,Rp[j].x, Rp[j].y));
if(k==1)dp[i][j][1] = Math.min(dp[i][j-1][1]+Rp[j].y-Rp[j-1].y, dp[i][j-1][0]+cal(Lp[i].x, Lp[i].y,Rp[j].x, Rp[j].y));
}
}
}
System.out.printf("%.2f",Math.min(dp[L][R][0]+cal(Lp[L].x, Lp[L].y, ex, ey), dp[L][R][1]+cal(Rp[R].x, Rp[R].y, ex, ey)));
}
private static double cal(double x1, double y1, double x2, double y2) {//计算距离
double t1 = Math.pow(x1-x2,2);
double t2 = Math.pow(y1-y2,2);
return Math.sqrt(t1+t2);
}
}
class point{
double x;
double y;
public point(double x,double y) {
this.x=x;
this.y=y;
}
}