【题解】CSP-J2019第二轮题解

CSP-J2019第二轮题解

代码在最后

T1.数字游戏 ⊗ \otimes

可以利用 for 循环遍历字符串的每一位并统计答案。

T2.公交换乘 ⊗ \otimes

分地铁和公交车来考虑。
乘坐地铁的钱是不可优惠的,所以可以先将做地铁的钱先加起来,并且用一种容器存储一张优惠票的信息:得到的时间 t t t 以及这次地铁的价格 p p p。由于只需要存两个信息,我们可以选用 pair 容器。题目中提到要用最早的优惠票,故可以将其放入一个可以自动排序的容器中,将 p p p 作为第一关键字, t t t 作为第二关键字,即将所有的 ( p , t ) (p,t) (p,t) 存入可以自动排序的容器中。
乘坐公交车的钱是可以优惠的。我们要考虑当前这班公交车使用哪张优惠票。前文提到,优惠票存在了会自动排序的仪器中,所以选择尽量靠前的票即可,但要注意将过时的票删去,将价格高于公交车票钱的票忽略。
还有一个问题没有解决:将优惠票存入什么容器当中?这个容器有如下特点:1.能自动排序。2.支持删去任意一个元素。于是,自然而然的就想到了 set

T3.纪念品 ⊗ \otimes

动态规划题,有一点点的小巧妙。
动态规划的第一步就是提出一种最简单、低效的方法,后面再慢慢优化。根据题意可以得到如下方法:设 f i , j , k f_{i,j,k} fi,j,k 表示前 i i i 天考虑前 j j j 个纪念品并且有 k k k 元钱的时候可以赚到的最多的钱。对于每个纪念品,可以看成是买来之后第二天马上卖掉,因为每天可以无限买入与卖出。于是,得转移方程: f i , j , k = max ⁡ ( f i − 1 , j , k , f i − 1 , j , k − p i − 1 , j + p i , j ) f_{i,j,k}=\max(f_{i-1,j,k},f_{i-1,j,k-p_{i-1,j}}+p_{i,j}) fi,j,k=max(fi1,j,k,fi1,j,kpi1,j+pi,j)。根据背包的思想,前两维可以省去(没学过背包的可以这么理解:直接去掉前两维,这个位置之前的值就可以作为三维转移式中的前一项)。这道题就是完全背包,体积为 p i − 1 , j p_{i-1,j} pi1,j,价值为 p i , j p_{i,j} pi,j,理解为昨天花钱买,今天得到那么多钱,前提是当前的钱数大于昨天的价格。
这题的巧妙之处在于两点:1.在省去了两维之后,变成了完全背包的裸题。2.根据题目的性质可以将“纪念品”这一概念抛弃,转化为转移时的介质。
正解思路如下:设 f i f_i fi 表示有 i i i 元钱时能获得的最多钱数。天数从 1 ∼ n 1\sim n 1n 枚举,将每一个纪念品看成是体积为前一天价格,价值为今天的价格,做一个完全背包即可。 f j = max ⁡ ( f j , f j − p i − 1 , j + p i , j ) f_j=\max(f_j,f_{j-p_{i-1,j}}+p_{i,j}) fj=max(fj,fjpi1,j+pi,j)

T4.零件加工 ⊗ \otimes

图论题,先讲一点题外话。我个人总是认为同一场比赛里的图论题比动态规划题简单,主要是图论比较具象,对于一个问题知道该往哪里去想。这题还需要一个转化,转化完之后的问题就非常有意思了。
假设一个人要生产 l l l 级零件,那么旁边的人就要生产 l − 1 l-1 l1 级的零件。这个过程很重要,是转化的一把钥匙。在这个过程中,每经过一根传送带,生产的等级降了一级。看上去很显然,但是转化的思路就随之出来了:如果要提供原材料,可以看成是生产 0 0 0 级零件,而一开始某个人要生产 l l l 级零件,期间共降了 l l l 级,就说明要经过 l l l 条传送带,问题就转化为是否存在一条源点到给定点的长度为 l l l 的路径
首先,可以自然而然的想到先求最短路,然后先让这个零件传递这么多次,到了给定点之后可以通过“来回耗”的方式一次性降低 2 2 2 级。例如样例 1 1 1 1 ∼ 3 1\sim3 13 的最短路是 2 2 2,要判断 3 3 3 号节点生产 16 16 16 级零件时 1 1 1 号节点是否需要提供,那么零件从 3 3 3 传递到 1 1 1 降了 2 2 2 级,剩余 14 14 14 级。路径 1 → 2 → 1 → 2 → ⋯ → 1 1\to2\to1\to2\to\cdots\to1 12121 可以做到“来回一次减少 2 2 2 级”的效果,所以 1 1 1 号节点需要提供原材料。
如果你在思考的话,你会轻而易举的举出反例。样例 2 就是一个很好的反例,你可以试一试 2 4 \text{2 4} 2 4 这一次询问。如果你在思考的话,你会轻而易举的发现错误的原因:奇环。奇环使得有奇偶两种路径,并且这道题 Yes/No 的关键点就在于奇偶。所以说,在没有奇环的情况下,这个算法是正确的。
当然,我们可以解决这个问题。有一种叫做“奇偶最短路”的算法,就是分别算出源点到所有点的长度为奇数、偶数的最短路。这里推荐将 Dijkstra 单源最短路进行修改。令 d [ 0 / 1 ] [ u ] d[0/1][u] d[0/1][u] 表示源点到点 u u u 且路径长度为 偶/奇 的最短路。如果你在思考,也许你就知道怎么做了。如果你还不知道的话,你就得想一想自己是否理解了 Dijkstra 算法的本质或者你有没有好好思考这题。由于一条边的长度为 1 1 1,设 ( u → v ) = 1 (u\to v)=1 (uv)=1,且 u u u 为已知路径的点,则 d [ 0 ] [ v ] = min ⁡ ( d [ 0 ] [ v ] , d [ 1 ] [ u ] + 1 ) d[0][v]=\min(d[0][v],d[1][u]+1) d[0][v]=min(d[0][v],d[1][u]+1) d [ 1 ] [ v ] = min ⁡ ( d [ 1 ] [ v ] , d [ 0 ] [ u ] + 1 ) d[1][v]=\min(d[1][v],d[0][u]+1) d[1][v]=min(d[1][v],d[0][u]+1)。不熟悉的同学也不要紧哈,自己上网查一查资料,会学会的。这个算法不难。

代码

T 1 : T1: T1:

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= ch == '-' ? -1 : 1, ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = 10 * s + ch - '0', ch = getchar());
	return s * w;
}
signed main(){
	string s;
	cin >> s;
	int cnt = 0;
	for(int i = 0; i < 8; i++) cnt += s[i] - '0';
	cout << cnt << endl;
	return 0;
}

T 2 : T2: T2:

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= ch == '-' ? -1 : 1, ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = 10 * s + ch - '0', ch = getchar());
	return s * w;
}
const int MAXN = 100005;
set<pair<int, int> > st;
signed main(){
	int T = read(), ans = 0;
	for(int i = 1, op, p, t; i <= T; i++){
		op = read(), p = read(), t = read();
		if(op == 0){
			ans += p;
			st.insert(make_pair(t, p));
		} else {
			bool flag = false;
			while(!st.empty() && st.begin() -> first + 45 < t) st.erase(*st.begin());
			for(set<pair<int, int> >::iterator it = st.begin(); it != st.end(); it++){
				if(it -> second >= p){
					st.erase(*it);
					flag = true;
					break;
				}
			}
			if(flag == false) ans += p;
		}
//		cout << ans << endl;
	}
	cout << ans << endl;
	return 0;
}

T 3 : T3: T3:

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= ch == '-' ? -1 : 1, ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = 10 * s + ch - '0', ch = getchar());
	return s * w;
}
const int MAXN = 105;
const int MAXT = 105;
const int MAXM = 10005;
int n, m, t, a[MAXN][MAXT], dp[MAXM];
signed main(){
	t = read(), n = read(), m = read();
	for(int i = 1; i <= t; i++){
		for(int j = 1; j <= n; j++){
			a[j][i] = read();
		}
	}
	for(int date = 1; date < t; date++){
//		cout << "Day " << date << " m=" << m << endl;
		for(int i = 1; i <= m; i++){
			dp[i] = i;
		}
		for(int i = 1; i <= n; i++){
			for(int j = a[i][date]; j <= m; j++){
				dp[j] = max(dp[j], dp[j - a[i][date]] + a[i][date + 1]);
			}
//			for(int j = 1; j <= m; j++) cout << dp[j] << " ";
//			cout << endl;
		}
//		cout << endl;
		m = dp[m];
	}
	cout << m << endl;
	return 0;
}

T 4 : T4: T4:

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= ch == '-' ? -1 : 1, ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = 10 * s + ch - '0', ch = getchar());
	return s * w;
}
const int MAXN = 100005;
struct Edge{
	int to, nxt;
} e[MAXN << 1];
int n, m, d[2][MAXN], head[MAXN], tot;
void add(int u, int v){
	e[++tot].to = v;
	e[tot].nxt = head[u];
	head[u] = tot;
}
signed main(){
	int T; 
	n = read(), m = read(), T = read();
	for(int i = 1, u, v; i <= m; i++){
		u = read(), v = read();
		add(u, v);
		add(v, u);
	}
	for(int i = 1; i <= n; i++) d[1][i] = d[0][i] = 0x3f3f3f3f;
	priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > pq;
	pq.push(make_pair(0, 1));
	d[0][1] = 0;
	int size = 0x3f3f3f3f;
	while(!pq.empty()){
		int u = pq.top().second;
		pq.pop();
		for(int i = head[u], v; i; i = e[i].nxt){
			v = e[i].to;
			if(d[0][u] + 1 < d[1][v]){
				d[1][v] = d[0][u] + 1;
				pq.push(make_pair(d[1][v], v));
			}
			if(d[1][u] + 1 < d[0][v]){
				d[0][v] = d[1][u] + 1;
				pq.push(make_pair(d[0][v], v));
			}
		}
	}
	while(T--){
		int x = read(), l = read();
		if(d[l % 2][x] <= l) cout << "Yes" << endl;
		else cout << "No" << endl;
	}
	return 0;
}

后记

马上就要 CSP-J/S2022 第二轮了,相信很多同学都在抓紧训练。我也想说几句,就是要注意一下态度。你的代码是否会出现被注释的语句呢?你会为了调试而使得 89 89 89 行的代码变成 97 97 97 行吗?这些应该是基本的。在比赛的过程中为了拿到更多的分数,不仅是为了拿到名次,更是为这一年你对他付出的精力的回报。比赛来临在即,大家一起加油吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对不起,由于我是一个文本交互的模型,我无法提供图像内容或直接链接到具体的题解或解决方案。但是,我可以帮你理解CSP-J2019公交换乘问题的基本概念和解决策略,这通常涉及到数据结构、图论以及算法设计。 CSP-J2019中的公交换乘问题可能是一个典型的旅行商问题(Traveling Salesman Problem, TSP)变种,或者是寻找最优路径的问题,其中涉及到公交网络中不同站点之间的最短路径或最少换乘次数。解决此类问题通常需要使用动态规划、贪心算法或者一些启发式搜索算法,比如A*搜索或Dijkstra算法。 如果你需要了解题目的基本思路,可能会这样操作: 1. 建立一个图,节点代表公交站点,边代表两个站点之间的路线及其长度或换乘次数。 2. 对于每个节点,计算从起点到所有其他节点的最短路径,形成一个邻接矩阵或邻接表。 3. 使用动态规划方法,例如记忆化搜索,尝试所有可能的路径,每次选择当前未访问节点中距离最近的一个,直到遍历完所有节点并回到起点,记录下总的距离或换乘次数。 4. 为了优化,可以考虑使用启发式搜索策略,如用估算的总距离作为启发信息,优先探索看起来更优的路径。 如果你对具体解法有疑问,或者想了解某个步骤的详细操作,请告诉我,我会尽力解释。至于详细的题解,建议你查阅相关的代码库、论坛帖子或在线教程,它们通常会有文字描述和步骤示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值