日期: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,目标是找到有最多这样房间的飞行弹道。
下面将举个栗子来解释它:
对这个地图来说:
它有 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;
}
四、反思&总结:
还得多注意特殊数据啊!!!