CSP-J模拟赛补题5

日期:2024年10月5日

学号:S14738

一、我的总分:
T1【牛奶(milk)】:100分

T2【树组(Traary)】:5分

T3【智乃的兔子(usagi)】:0分

T4【一颗成熟的奥术飞弹(missiles)】:0分

二、比赛概况:
T1【牛奶(milk)】:

        看完题觉得挺简单,不就是结构体和排序吗!噼里啪啦打了将近40行代码,也是成功的给其他人造成了危机感 [ 哈哈哈 ] ,最后也是AC了。

T2【树组(Traary)】:

        以为看错题了,应该是“数组”,又看了一遍发现真的是“树组”,也是成功的被这道题整懵了,直接for循环,答案错误 WA (wonderful answer)。

T3【智乃的兔子(usagi)】:

        这几次的第三题都好难好难啊啊啊!完全不会,靠运气骗骗分

T4【一颗成熟的奥术飞弹(missiles)】:

        首先,我不知道什么是奥术,其次,我不知道什么是飞弹,所以这道题我肯定是不会的。

三、比赛分析:

T1【牛奶(milk)】:

1、题目大意

        每天一杯奶,健壮切题人。

        Meowowco 有每天喝牛奶的习惯,因为牛奶是膳食中蛋白质、钙、磷、维生素 A、维生素 D 和维生素 B2 的重要来源之一,可以让经常出勤的 Meowowco 变得健壮。因此她每个月都会去买一些牛奶屯在冰箱里。

        今天又到了采购的日子,Meowowco 又来到了熟悉的超市,看着冰箱里陈列着价格不同的牛奶,她摸了摸自己的钱包,

        “糟糕,出勤花了太多的钱了。。。。。。”

        不过这都不是问题,毕竟又不是把所有钱都花完了,只是预算被压缩了。现在问题来了,冰箱里有 n 个种类的牛奶,它们有各自的数量 a_i​​ 和价格 b_i​​。作为一只学过动态规划的猫,Meowowco 一个月需要 m 盒牛奶,她想知道屯够一个月的牛奶量的最小开销。

2、考试思路

       输入 -> 排序 -> 计算 -> 输出

3、讲解后思路

        考试思路非常OK

4、AC代码

#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
long long n,m,sum;
struct node{
	long long sl,pr;
}milk[1000005];
bool cmp(node a,node b){
	if(a.pr == b.pr){
		return a.sl > b.sl;
	}
	return a.pr < b.pr;
}
int main(){
//	freopen("milk.in","r",stdin);
//	freopen("milk.out","w",stdout);
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		scanf("%d%d",&milk[i].sl,&milk[i].pr);
	}
	//排序:价格从低到高 
	sort(milk+1,milk+1+n,cmp);
	for(int i = 1;m > 0;i++){
		//判断第 i种牛奶够不够 m个 
		if(m >= milk[i].sl){
			sum+=milk[i].sl*milk[i].pr;
			m-=milk[i].sl;
		}else{
			sum+=m*milk[i].pr;
			m = 0;
		}
	}
	cout << sum;
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

T2【树组(Traary)】:

1、题目大意

        Meowowco 有 n 棵树苗,今天要在数组的每一个位置种(物理)上一棵树。种好之后,我们称它为树组。

        最开始,树组中所有的树的高度为 0。每天过后,每棵树会自然生长 1 单位高度。

        Meowowco 的种树过程持续 m 天,在每一天早上,她有三种操作:

        op=1:选择某棵树 x 对其施展魔法,该效果持续 k 天(包括当天)。拥有魔法效果的树每天晚上会额外生长 1 单位高度。若施展时该树已经存在魔法效果,则覆盖原来的魔法效果(也就是取消原来的魔法效果,加上这次的魔法效果)。

        op=2:选择取消某棵树 x 的魔法效果,可能会对没有施加魔法的树进行操作。

        op=3:Meowowko 想知道该天某棵树 x 的高度。

        对于每个 op=3,输出一个整数 h,代表该树的高度。

2、考试思路

        for循环,喜提答案错误大礼包一份

3、讲解后思路

        输入 -> 循环 -> 判断并计算 -> 输出

4、AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
vector< pair<int,int> >vec[110000];
int n,m,k,a[110000],ans[110000];
int main(){
//	freopen("Traary.in","r",stdin);
//	freopen("Traary.out","w",stdout);
	memset(ans,-1,sizeof(ans));
	cin >> n >> m >> k;
	for(int i = 1;i <= m;i++){
		int op,x;
		cin >> op >> x;
		vec[x].push_back({op,i});
	}
	//枚举每棵树 
	for(int i = 1;i <= n;i++){
		int p = -1,add = 0;
		//枚举操作 
		for(int j = 0;j < vec[i].size();j++){
			int x = vec[i][j].first,y = vec[i][j].second;
			if(x == 1){
				if(p > 0){
					//p是上一次操作时间,如果 y-p > k则魔法已失效,最多增加 k高度 
					add += min(y-p,k);
				}
				//记录 
				p = y;
			}else if(x == 2){
				if(p > 0){
					add+=min(y-p,k);
				}
				p = -1;
			}else{
				//第 i天高度 i-1 
				ans[y] = y-1+add+(p>0?min(y-p,k):0);
			}
		}
	}
	for(int i = 1;i <= m;i++){
		if(ans[i] >= 0){
			cout << ans[i] << endl;
		}
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

T3【智乃的兔子(usagi)】:

1、题目大意

        Chino 是一个可爱的初中生,超喜欢兔子 (和 Cocoa) …!?精通咖啡,并且能干可靠。

        今天 Chino 在梦境世界中被可爱的兔子环绕,它们都是这个梦境世界的卡密——Cocoa 的使徒。每一只棉花糖般的兔子都有一个可爱值 a_i。

        “超想和可爱的小兔子们贴贴 ∼∼”

        因此她向 Cocoa 许愿:请让我挑选出一些可爱的兔子。

        但是,Cocoa 并不希望 Chino 随意挑选兔子,她希望 Chino 挑选出的兔子的可爱值的和是 7 的倍数。Cocoa 作为这个世界的卡密,觉得仅有这一条挑选规则会让游戏变得很无趣,她制定规则的目的,很可能是,吃掉 Chino…!?于是她又增加了一条规则:

        Cocoa 亲吻了所有的兔子,它们都受到了”祝福”,当 Chino 选择这只兔子之后,她将会获得祝福值 b_i​​。当 Chino 拥有的祝福值超过 H 点时,会被 Cocoa 吃掉。

        Chino 想知道怎么样才能完成挑选可爱值和为 7 的倍数的兔子,可爱和最大的同时还不会被吃掉。

2、考试思路

        嘿嘿,又是没有思路的一道题呢

3、讲解后思路

        输入 -> 循环+判断+计算+数组赋值 -> 输出

4、AC代码

#include<iostream>
#include<cstdio>
using namespace std;
long long dp[2][11000][7],t[11000][7],f[1100][7],g[1100][7],ans;
int n,m,k,a[11000],b[11000];
int main(){
//	freopen("usagi.in","r",stdin);
//	freopen("usagi.out","w",stdout);
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		cin >> a[i];
	}
	for(int i = 1;i <= n;i++){
		cin >> b[i];
	}
	//幸福值不可能超 
	if(m == 998244353){
		for(int i = 1;i < 7;i++){
			t[0][i] = -1e18;
		}
		for(int i = 1;i <= n;i++){
			for(int j = 0;j < 7;j++){
				t[i][j] = max(t[i-1][((j-a[i])%7+7)%7]+a[i],t[i-1][j]);
			}
		}
		//必须是 7的倍数 
		cout << t[n][0] << endl;
		return 0;
	}
	for(int i = 0;i < 1100;i++){
		for(int j = 0;j < 7;j++){
			//初始化不可能 
			g[i][j] = -1e18;
		}
	}
	g[0][0] = 0;
	//n个兔子 
	for(int i = 1;i <= n;i++){
		//祝福值限制为 m 
		for(int w = 0;w <= m;w++){
			//遍历余数情况 
			for(int j = 0;j < 7;j++){
				f[w][j] = g[w][j];
				if(w >= b[i]){
					f[w][j] = max(g[w-b[i]][((j-a[i])%7+7)%7]+a[i],f[w][j]);
				}
			}
		}
		swap(f,g);
	}
	for(int i = 0;i <= m;i++){
		ans = max(ans,g[i][0]);
	}
	cout << ans;
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

T4【一颗成熟的奥术飞弹(missiles)】:

1、题目大意

        Meowowco 正在玩一款未知的 1V1 RTS 游戏,游戏创建后会随机创建一个有 n 个房间的地图,由 m 条通道相连,房间与房间之间最多只有一个通道,直接由通道相连的房间的距离可以记为 1,整张地图所有房间两两可达。

        Meowowco 出生在编号为 1 的房间,而她的对手出生在编号为 n 的房间。现在 Meowowco 需要创造军队或者释放技能去击败对手,不过今天她有着更高级的黑魔法加持(指自瞄),令”奥术飞弹”变成”成熟的奥术飞弹”,只释放”奥术飞弹”就可以获得胜利。

        作为一颗成熟的奥术飞弹,不管当前处于哪个房间,都会瞬间规划好 一条 前往目标所在位置的最短飞行弹道(当然,最短飞行弹道有时候并不是唯一的,所以有多条最短飞行弹道时会随机选择一条),如果它没有沿着当前房间规划好的最短飞行弹道飞行,记为偏离轨迹 1 次。

        作为一颗成熟的奥术飞弹,应该会自己计算一条有着“大可能性“的飞行弹道。由于有些房间的最短飞行弹道不唯一,飞行弹道可能偏离也可能不偏离,”大可能性”飞行弹道要求 可能产生的偏离数尽可能大

        一个房间如果有多条通往目标的最短飞行弹道,则可能偏离数增加 1,目标是找到有最多这样房间的飞行弹道。

        下面将举个栗子来解释它:

        对这个地图来说:

image-20240918174721172

        它有 3 条从 1 到 6 的最短飞行弹道:

                1 => 2 => 4 => 6
                1 => 3 => 4 => 6
                1 => 3 => 5 => 6

        假如你现在处于房间 1,那么你可以选择房间 2 和房间 3 ,因为 2 和 3 都在最短飞行弹道上,所以不管前往哪个房间都会让可能偏离数增加,此时可能偏离数为 1;

        当飞弹选择 1=>2 时,接下来它只能沿着 2 => 4 => 6,飞行,因此这条弹道的可能偏离数为 1。

        当飞弹选择 1 => 3 时,接下来的房间 4 和 5 都处于最短飞行弹道上,不管前往哪个房间都会让可能偏离数增加,此时可能偏离数增加到 2;之后都只有一条弹道到达 6,因此这两条弹道的可能偏离数为 2。

        综上所述,

                1 => 2 => 4 => 6 的可能偏离数为 1。
                1 => 3 => 4 => 6 的可能偏离数为 2。
                1 => 3 => 5 => 6 的可能偏离数为 2。

        最大的可能偏离数为 2。

        寻找出所有的最短飞行弹道,每条最短飞行弹道都有一个可能偏离数,找到其中最大的可能偏离数,输出最短飞行弹道条数和最大的可能偏离数。


简而言之,就是 n 个点的无向连通图,无重边和自环,目标是从 1 点到达 n 点。在一个点上,如果有多条最短路径到达终点,则 可能偏离数 加一,求出最短路径的总条数,以及所有最短路中可能偏离数的最大值。

2、考试思路

        你会吗?反正我不会,根本没有思路好吧

3、讲解后思路

        输入 -> 深搜 -> 循环+计算 -> 输出

4、AC代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#define vint vector<int>
using namespace std;
vint son[110000],vec;
int fa[110000],dis[110000],ans[110000],cnt[110000],n,m;
bool vis[110000];
queue<int> q;
//逆序记录 
void bfs(){
	q.push(n);
	vis[n] = 1;
	while(!q.empty()){
		int x = q.front();
		q.pop();
		//记录广搜序列 
		vec.push_back(x);
		for(int i = 0;i < son[x].size();i++){
			//孩子节点 
			int y = son[x][i];
			if(!vis[y]){
				vis[y] = 1;
				q.push(y);
				fa[y] = x;
				dis[y] = dis[x]+1;
			}
		}
	}
}
int main(){
//	freopen("missiles.in","r",stdin);
//	freopen("missiles.out","w",stdout);
	cin >> n >> m;
	for(int i = 1;i <= m;i++){
		int x,y;
		cin >> x >> y;
		//链式存储 
		son[x].push_back(y);
		son[y].push_back(x);
	}
	bfs();
	//记录最短飞行弹道条数 
	cnt[n] = 1;
	//近到远考虑点 
	for(int i = 0;i < vec.size();i++){
		int flag = 0,x = vec[i];
		for(int j = 0;j < son[x].size();j++){
			int y = son[x][j];
			//相邻两个点距离差 1说明在最短路边上 
			if(dis[x] == dis[y]+1){
				ans[x] = max(ans[x],ans[y]);
				cnt[x]+=cnt[y];
				cnt[x]%=998244353;
				if(y != fa[x]){
					flag = 1;
				}
			}
		}
		ans[x]+=flag;
	}
	cout << cnt[1] << " " << ans[1];
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

四、反思&总结:

        还得多注意特殊数据啊!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值