马术比赛
【题目描述】
小C将要去完成一项马术比赛,比赛的场地由n条赛道组成,这些赛道自上而下从1编号至n,每条赛道分成m段,每一段一定是一块平地或者障碍。小C对于他的马有着特殊的驾驭,这使得他在比赛中每一次前进的长度为至少L段,至多H段,而且必须落到平地上,如果一条赛道的起点或终点是障碍,那么这条赛道不能通行。赛道可以被从左至右或从右至左完成,但全程只能朝同个方向进行,小C需要从第一段开始前进,抵达最后一段才能视为完成赛道。
每条赛道都有一个技术评分,这个评分取决于这条赛道障碍排列的难度,这个数可以是一个负数。每当小C完成了一条赛道时,他就把这条赛道的技术评分加入他的总得分中。
小C完成整个比赛需要遵守如下的规则:
-
一开始,小C可以在前T条赛道中任选一条进入,从左往右完成这条赛道。
-
假如小C刚刚完成了编号为X的赛道,则接下来他必须进入编号在[X+1,X+T]范围内的赛道。
-
假如小C刚刚从左往右完成了一条赛道,那他接下来必须从右往左完成赛道,反之亦然。也就是说他以蛇形的顺序完成比赛。
-
为了完成比赛,小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
【数据范围和提示】
见下表
样例解释:
-
选择第2条赛道进入,从左至右完成,得分5
-
选择第4条赛道进入,从右至左完成,得分-5,累计得分0。
-
从左至右完成第五条赛道,最后得分9。
-
第二步不选择第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;
}
思路
上一个代码,就是
这道题需要判断每一行是否可以走,由于正着走和倒着走都是一样的,所以直接判断一个方向就好了。
这里我们需要用类似于单调队列的优化,每次取出前面一个区间
具体见代码。
代码
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!