马术比赛

【题目描述】

小C将要去完成一项马术比赛,比赛的场地由n条赛道组成,这些赛道自上而下从1编号至n,每条赛道分成m段,每一段一定是一块平地或者障碍。小C对于他的马有着特殊的驾驭,这使得他在比赛中每一次前进的长度为至少L段,至多H段,而且必须落到平地上,如果一条赛道的起点或终点是障碍,那么这条赛道不能通行。赛道可以被从左至右或从右至左完成,但全程只能朝同个方向进行,小C需要从第一段开始前进,抵达最后一段才能视为完成赛道。

每条赛道都有一个技术评分,这个评分取决于这条赛道障碍排列的难度,这个数可以是一个负数。每当小C完成了一条赛道时,他就把这条赛道的技术评分加入他的总得分中。

小C完成整个比赛需要遵守如下的规则:

  1. 一开始,小C可以在前T条赛道中任选一条进入,从左往右完成这条赛道。

  2. 假如小C刚刚完成了编号为X的赛道,则接下来他必须进入编号在[X+1,X+T]范围内的赛道。

  3. 假如小C刚刚从左往右完成了一条赛道,那他接下来必须从右往左完成赛道,反之亦然。也就是说他以蛇形的顺序完成比赛。

  4. 为了完成比赛,小C必须完成编号为n的赛道。

现在请你为他编排一个最佳的赛道顺序选择,使他得到最高的总得分,如果他不能够完成比赛,请你输出“impossible!”。

注:如果将一条赛道的每段从左至右编号为1到m,每一步的长度就是起点和终点的编号之差。

【输入】

第一行为五个正整数n,m,L,H,T。

第二行是n个整数a1,a2,a3…an,表示每条赛道的评分。

接下来n行,每行m个字符‘0’或‘1’,‘0’表示平地,‘1’表示障碍。

【输出】

一个整数表示最高的总得分,或者是“impossible!”。

【样例输入】

5 10 2 3 2

8 5 5 -5 9

0111010110

0000011010

0010001100

0010010010

0000110110

【样例输出】

9

【数据范围和提示】

见下表

样例解释:

  1. 选择第2条赛道进入,从左至右完成,得分5

  2. 选择第4条赛道进入,从右至左完成,得分-5,累计得分0。

  3. 从左至右完成第五条赛道,最后得分9。

  4. 第二步不选择第3条赛道是由于其无法完成。

代码

献上我丑陋的暴力代码:

#include <iostream>
#include <cstring>
#include <unordered_set>
#include <queue>
using namespace std;
const int N = 10010,M = 1010;
int n,m,l,h,t;
bool g[M];
int v[N];
int f[N];
bool a[N];
bool is_valid () {
	if (g[1] || g[m]) return false;
	memset (f,false,sizeof (f));
	f[1] = true;
	for (int i = 1;i <= m;i++) {
		if (g[i]) continue;
		for (int j = l;j <= min (i,h);j++) {
			if (!g[i - j]) f[i] |= f[i - j];
		}
	}
	return f[m];
}
int main () {
	cin >> n >> m >> l >> h >> t;
	for (int i = 1;i <= n;i++) cin >> v[i];
	unordered_set <int> s;
	for (int i = 1;i <= n;i++) {
		for (int j = 1;j <= m;j++) {
			char ch;
			cin >> ch;
			g[j] = ch - '0';
		}
		a[i] = is_valid ();
		if (a[i]) s.insert (i);
	}
	memset (f,-1,sizeof (f));
	if (!s.count (n)) {
		puts ("impossible!");
		return 0;
	}
	bool flag = false;
	for (int i = 1;i <= t;i++) {
		if (s.count (i)) {
			f[i] = v[i];
			flag = true;
		}
	}
	if (!flag) {
		puts ("impossible!");
		return 0;
	}
	for (int i = 1;i <= n;i++) {
		if (!s.count (i)) continue;
		for (int j = 1;j <= min (i,t);j++) {
			if (s.count (i - j)) f[i] = max (f[i],f[i - j] + v[i]);
		}
	}
	if (f[n] == -1) puts ("impossible!");
	else cout << f[n] << endl;
	return 0;
}

思路

上一个代码,就是is_valid太慢了,我们来加点优化:
这道题需要判断每一行是否可以走,由于正着走和倒着走都是一样的,所以直接判断一个方向就好了。
这里我们需要用类似于单调队列的优化,每次取出前面一个区间[max(imaxx1,0),max(iminx,0)]的和,这里加max是防止超出0
具体见代码。

代码

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 10010,M = 1010;
int n,m,minx,maxx,t;
bool g[M];
int v[N];
int f[N];
bool a[N];
bool is_valid () {
	if (g[1] || g[m]) return false;  //头节点和尾节点都确保能走到
	memset (f,0,sizeof (f));
	f[1] = 1;
	for (int i = 2;i <= m;i++) {
		int l = max (i - maxx - 1,0),r = max (i - minx,0);
		if (!g[i] && f[r] - f[l] > 0) f[i] = f[i - 1] + 1;  //如果前面又合法的点,就直接走过来
		else f[i] = f[i - 1];  //否则直接更新f[i]
	}
	return f[m] > f[m - 1];
}
int main () {
	cin >> n >> m >> minx >> maxx >> t;
	for (int i = 1;i <= n;i++) cin >> v[i];
	for (int i = 1;i <= n;i++) {
		for (int j = 1;j <= m;j++) {
			char ch;
			cin >> ch;
			g[j] = ch - '0';
		}
		a[i] = is_valid ();
	}
	memset (f,-1,sizeof (f));
	if (!a[n]) {
		puts ("impossible!");
		return 0;
	}
	bool flag = false;
	for (int i = 1;i <= t;i++) {
		if (a[i]) {
			f[i] = v[i];
			flag = true;
		}
	}
	if (!flag) {
		puts ("impossible!");
		return 0;
	}
	for (int i = 1;i <= n;i++) {
		if (!a[i]) continue;
		for (int j = 1;j <= min (i,t);j++) {
			if (a[i - j]) f[i] = max (f[i],f[i - j] + v[i]);  //暴力
		}
	}
	if (f[n] == -1) puts ("impossible!");
	else cout << f[n] << endl;
	return 0;
}
posted @   incra  阅读(132)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示