2017ICPC焦作现场赛部分题解(D、F)

博客解析了2017年ICPC焦作现场赛的两道题目——D题'Keiichi Tsuchiya the Drift King'和F题'Honeycomb'。D题涉及赛车漂移,通过几何分析找出赛车在不同弯道角度下所需的最窄赛道宽度。F题是一个蜂巢地图问题,要求找出从起点到终点的最短路径中经过的蜂窝数量。解决方案分别给出了漂移问题的几何思路和蜂巢问题的BFS算法应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D. Keiichi Tsuchiya the Drift King:

输入:

4
1 2 2 120
1 2 2 60
1 2 2 30
1 2 2 15

输出:

1.605551275464
1.605551275464
1.598076211353
1.415415569072

题意

       将赛车简化为一个长为 b,宽为 a 的木块。给出弯道半径 r 以及弯道角度 d。若赛车向右转弯漂移时始终保持右前方紧贴弯道,问最窄的赛道宽度为多少能够使得赛车漂移过程中始终处于赛道中。

思路

       根据样例大概可以猜测出当弯道角度大于一定程度的时候同一辆车所需要的最小宽度保持不变,当赛道弯度减小时所需要的赛道宽度也越小(想象一下如果赛道弯度为0度时只需要车宽就足够,当赛道弯度逐渐增大时,漂移过弯车尾离赛道右侧的距离就越大)。而整辆车在漂移的时候距离赛道右边最远的地方就是答案,最远的部分为整辆车的左后方。

上图为两种情况中简单的一种,车身距离弯道中心最远的点与中心的连线(图中蓝线)与弯道末尾与中心的连线(竖直棕线)的夹角设为 \theta,此时 \theta < d,所求w在图中可以直观看出为勾股定理求出斜边长 - r。

 

  

此时左下角与圆心连线与右上角和圆心连线的夹角 \theta 大于 d ,这种情况将\theta - d角设为\varphi,则实际道路宽只需要\sqrt{b^{2} + (a + r)^{2}} \cdot \cos \varphi - r这么宽。当 d、b、a如上图(右)所示时,只漂移了一定角度就直行,因此所需要的宽度小于第一种情况。

AC代码:

/*---------------------------------
 *File name: D.cpp
 *Author: Snpilola
 *Creation date: 2019-10-03 18:41
 *-------------------------------*/
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define mkp make_pair
#define PLL pair<LL, LL>
#define lowbit(x) x & (-x)
#define PII pair<int, int>
#define Pque priority_queue 
using namespace std;
const int maxn = 1e3 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int EPS = 1e-6;
const double PI = acos(-1.0);

//ICPC 焦作D

int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		double a, b, r, d;
		scanf("%lf %lf %lf %lf", &a, &b, &r, &d);
		d = d / 180 * PI;
		double D = atan(b / (a + r));
		if(D <= d) printf("%.12lf\n", sqrt((a + r) * (a + r) + b * b) - r);
		else{
			double E = D - d;
			double ans = sqrt(((a + r) * (a + r)) + (b * b)) * cos(E) - r;
			printf("%.12f\n", ans);
		}
	}
	return 0;
}

F. Honeycomb

输入:

1
3 4
  +---+       +---+
 /     \     /     \
+       +---+       +---+
 \           \     /     \
  +   +   S   +---+   T   +
 /     \     /           /
+       +---+       +   +
 \           \     /     \
  +---+       +---+       +
 /                       /
+       +---+       +   +
 \                 /     \
  +---+       +---+       +
       \     /     \     /
        +---+       +---+

输出:

7

题意:给出蜂巢的二维地图,给出起点终点,问从起点走到终点的最短路中会走过几个蜂窝。包括起点终点。

思路:简单BFS,只是方向改一改。

坑点:用memset容易T,观察发现只有几个点会被vis标记,只需要将所有可能被标记的点初始化为0就可以。

AC代码:

/*---------------------------------
 *File name: F.cpp
 *Team: 这题太简单啦
 *Author: Snpilola
 *Creation date: 2019-10-03 18:41
 *-------------------------------*/
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define mkp make_pair
#define PLL pair<LL, LL>
#define lowbit(x) x & (-x)
#define PII pair<int, int>
#define Pque priority_queue 
using namespace std;
const int maxn = 1e3 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int EPS = 1e-6;

int r, c, n, m;
int sx, sy;
int d[6][2] = {{4, 0}, {-4, 0}, {-2, -6}, {-2, 6}, {2, -6}, {2, 6}};
int dd[6][2] = {{2, 0}, {-2, 0}, {-1, -3}, {-1, 3}, {1, -3}, {1, 3}};
int rnd[6][2] = {{0, 4}, {0, -4}, {2, 2}, {2, -2}, {-2, 2}, {-2, -2}};
bool vis[maxn * 4 + 10][maxn * 6 + 10];
char a[maxn * 4 + 10][maxn * 6 + 10];

struct NODE{
	int x, y, num;
	NODE(int x, int y, int st){
		this -> x = x;
		this -> y = y;
		this -> num = st;
	}
};

bool check(int x, int y, int i){
	if(x < 0 || x > n || y < 0 || y > m) return 0;
	if(vis[x][y] == 1) return 0;
	for(int i = 0; i < 6; i++){
		if(a[x + rnd[i][0]][y + rnd[i][1]] != '+') return 0;
	}
	return 1;
}

bool judge(int x, int y){
	if(x < 0 || x > n || y < 0 || y > m) return 0;
	if(a[x][y] == '\\' || a[x][y] == '/' || a[x][y] == '-') return 0;
	if(vis[x][y] == 1) return 0;
	return 1;
}

int Bfs(int x, int y){
	queue<NODE> q;
	NODE ac = NODE(x, y, 1);
	q.push(ac);
	while(!q.empty()){
		ac = q.front(); q.pop();
		int nx, ny, dx, dy;
		for(int i = 0; i < 6; i++){
			nx = ac.x + d[i][0],  ny = ac.y + d[i][1];
			dx = ac.x + dd[i][0], dy = ac.y + dd[i][1];
			if(check(nx, ny, i) && judge(dx, dy)){
				if(a[nx][ny] == 'T') return ac.num + 1;
				vis[nx][ny] = 1;
				q.push(NODE(nx, ny, ac.num + 1));
			}
		}
	}
	return -1;
}

int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		scanf("%d %d", &r, &c);
		getchar();
		sx = sy = -1;
		n = 4 * r + 3, m = 6 * c + 3;
		for(int i = 2; i <= maxn * 4 + 3; i += 4){
			for(int j = 4; j <= maxn * 6 + 3; j += 12){
				vis[i][j] = 0;
			}
		}
		for(int i = 4; i <= maxn * 4 + 3; i += 4){
			for(int j = 10; j <= m * 6 + 3; j += 12){
				vis[i][j] = 0;
			}
		}
		for(int i = 0; i < n; i++){
			gets(a[i]);
		}
		//for(int i = 0; i < n; i++) printf("%s\n", a[i]);
		for(int i = 0; i < n; i++){
			for(int j = 0; j < m; j++){
				if(a[i][j] == 'S'){
					sx = i;
					sy = j;
					break;
				}
			}
			if(sx != -1) break;
		}
		int ans = Bfs(sx, sy);
		printf("%d\n", ans);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值