Homecoming

题目连接: Homecoming

题目:

题目太长了, 在这里就不再复制了, 大概分析一下就是一个找连续相同字符的问题.

解题思路:

这个题的字符串来表示车站, 只有A和B两种车站. 如果在A类车站上车, 那么途径的所有A车站(期间不能有B车站)我们都可以选择下车, 如果当我们遇到B车站时, 只能选择下车. B类车站上车同理.
如题中举例: 对于串"AABBBAB", 我们从1开始标记, 到7为止.
如果我们从位置1上车, 那么我们最远可以坐到位置3, 我们也可以选择在位置2下车.
如果我们从位置2上车, 那么我们只能坐到位置3就下车.
如果我们从位置3上车, 那么我们最远可以坐到位置6, 但是我们也可以选择在4, 5位置下车.
其余情况同理.

题目中涉及到上A类车需要花费a元, 而B类车需要花费b元,而我们只有p元, 因此我们每次都应该坐的尽可能远, 减少倒车次数.
我们自然是希望能从位置1上车, 一直坐到终点的, 但是由于涉及到可能钱不够的问题, 因此我们可以考虑:
①从终点出发, 看看往回坐能坐到哪里.
②假设我们从位置1开始做, 每次上下车记录区间的花费, 最后倒着相加所有的花费, 直到得到满足题意的最小值为止.

思路分析1: 倒着跑(定下车点, 找上车点):

如果倒着跑, 我们细细分析, 会发现不能单纯的认为我们是从终点出发往起点跑.
假设我们从A类车站上车, 经历数个A类车站后到一个B类车站下车, 如: AAAAAB, 但是如果我们单纯的倒着跑, 我们认为自己从B车站上车, 但是只坐一站车, 即到下标为5的A车站就下车了, 显然我们模拟的情况是错误的.

但是如果我们经过分析, 其实我们会发现坐车大致上只有两种情况:
①我们是从同类车站上下车(到了终点)
②我们是从不同类车站上下车.

例如: AAAABBBB, 我们从A类车站(位置1)上车, 到B类车站(位置5)下车, 然后再从位置5坐到终点.
这个例子中: 第一次坐车就属于情况1, 第二次就属于情况2.

所以思路一倒着跑的难点在于: 找到真正的上车点, 至于下车点, 因为我们是倒着跑的, 所以下车点是我们的定点. 且我们知道初始的下车点就是终点, 后续的下车点是我们上一次找到的上车点.

倒着跑AC代码:

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN = 1E5 + 10;
int a, b, p;
int findon(char* s) {
	char* op = &s[strlen(s) - 1]; //表示下车点, 默认是终点位置
	while (true) {
		char* ptr = op - 1; bool flag = 0; //flag表示遇到了不同类车站 
		if (*ptr == '#') break; 

		/* 找到真正的上车点 */
		if (*ptr != *op) flag = 1; //说明他前面的是不同类车站, 则op为下车点
		//不同类车站, 要找到最初的不同类位置 情况二
		while (*ptr != *op && flag && *ptr != '#') ptr--;
		//同类车站, 要找到最初同类的位置 情况一
		while (*ptr == *op && !flag && *ptr != '#') ptr--;
		ptr++;
		const int cost = *ptr == 'A' ? a : b; 
		if (p - cost >= 0) p -= cost;
		else break;
		op = ptr;
	}
	return op - s;
}
int main(void)
{
	int t; cin >> t;
	while (t--) {
		scanf("%d %d %d", &a, &b, &p);
		char s[MAXN]; cin >> s + 1;
		s[0] = '#'; //作为开始标志
		if (a > p && b > p) { cout << strlen(s) - 1 << endl; continue; }
		cout << findon(s) << endl;
	}
	return 0;
}
思路分析2: 正着跑(定上车点, 找下车点):

如果你明白了倒着跑的思路, 正着跑相对而言要容易很多, 因为他没有拧着逻辑, 我们哪里上车, 哪里下车也不需要分情况, 与事实情况是相同的. 在这里就不多赘述了.

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN = 1E5 + 10;
int num[MAXN]; //记录开销
int index[MAXN]; //记录下车点
int main(void)
{
	int t; cin >> t;
	while (t--) {
		int a, b, p;
		scanf("%d %d %d", &a, &b, &p);
		char s[MAXN]; cin >> s + 1; s[0] = '5'; //这里如果从s[1]开始读入, 建议把s[0]随便初始化一下, 否则要WA
		if (a > p && b > p) { cout << strlen(s) - 1 << endl; continue; }

		char* op = s + 1; //表示当前所处的位置
		int cou = 0; //区间数目下标
		do {
			const int cost = *op == 'A' ? a : b; //当前位置上车花费是a还是b
			char* ptr = op + 1; //为了找到下车点
			if (*ptr == '\0') break;
			while (*ptr == *op && *ptr != '\0') ptr++;
			num[++cou] = cost; //记录当前车站所需的花费
			index[cou] = ptr - s; //记录下车位置 至于为什么记录下车点, 可以结合后面的for循环思考
			if (*ptr == '\0') index[cou]--; //到串尾了, 真正下车点--
			op = ptr; //让本次下车点作为下一次的上车点
		} while (*op != '\0');

		int res = -1;
		for (int i = cou; i >= 1; --i) {
			if (p - num[i] >= 0) p -= num[i];
			else { res = index[i]; break; } //这趟车做不起了, 我们应该从这趟车的下车点上车
		}
		if (res == -1) res = 1; //说明可以从初始位置开始坐.
		cout << res << endl;
	}
	return 0;
}

最近高产, 前一阵子太懒了, 冲冲冲!

END

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值