姓名:徐浩轩,校区:和谐校区,考试时间: 2024 2024 2024 年 10 10 10月 5 5 5日 09 : 00 : 00 09:00:00 09:00:00- 12 : 30 : 00 12:30:00 12:30:00,学号: S 08503 S08503 S08503
CSP-J Day 5 5 5 模拟赛补题报告
前言
考了我们班第8(170分)
T1 milk:
A
c
c
e
p
e
t
e
d
100
\color{green}Accepeted\space100
Accepeted 100
T2 traary:
W
r
o
n
g
_
a
n
s
w
e
r
40
\color{red}Wrong\_answer\space40
Wrong_answer 40
T3 box:
W
r
o
n
g
_
a
n
s
w
e
r
25
\color{red}Wrong\_answer\space25
Wrong_answer 25
T4 party:
W
r
o
n
g
_
a
n
s
w
e
r
5
\color{red}Wrong\_answer\space5
Wrong_answer 5
T1
题意
现在问题来了,冰箱里有 n 个种类的牛奶,它们有各自的数量 a[ i ] 和价格 b[ i ]。作为一只学过动态规划的猫,Meowowco 一个月需要 m 盒牛奶,她想知道屯够一个月的牛奶量的最小开销。
思路
暴力模拟,只要 m 不为 0,要使得最终价格最低,那么每次就选择价格最小的牛奶进行购买,注意购买后进行标记,后续不再购买,直至购买到 m 为 0 结束。
时间复杂度为 O( n * m ),注意开long long(1e10)
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m;
struct node{
int a,b;
}s[N];
bool cmp(node x,node y){
if(x.b==y.b){
return x.a>y.a;
}
return x.b<y.b;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>s[i].a>>s[i].b;
}
sort(s+1,s+n+1,cmp);
int ans=0;
for(int i=1;i<=n;++i){
if(m<=0){
break;
}
if(m>s[i].a){
m-=s[i].a;
ans=ans+s[i].a*s[i].b;
}
else{
ans=ans+m*s[i].b;
m=0;
}
}
cout<<ans;
return 0;
}
T2
题意
Meowowco 有 n 棵树苗,今天要在数组的每一个位置种(物理)上一棵树。种好之后,我们称它为树组。最开始,树组中所有的树的高度为 0。每天过后,每棵树会自然生长 1单位高度。Meowowco 的种树过程持续 m 天,在每一天早上,她有三种操作:
op=1:选择某棵树 x 对其施展魔法,该效果持续 k 天(包括当天)。拥有魔法效果的树每天晚上会额外生长 1 单位高度。若施展时该树已经存在魔法效果,则覆盖原来的魔法效果(也就是取消原来的魔法效果,加上这次的魔法效果)。
op=2:选择取消某棵树 x 的魔法效果,可能会对没有施加魔法的树进行操作。
op=3:Meowowko 想知道该天某棵树 x 的高度。
对于每个 op=3,输出一个整数 h,代表该树的高度。
思路
p[ i ]:记录第 i 棵树最近一次施法时间。如果当前要对 i 施加魔法,那么上一次施法时间就是 p[ i ],当前时间是枚举到的时间 t ,方便判断 t 和 p[ i ]之差是否已经超过 k ,超过 k 表示上次施法效果已经没有了,不超过可以继续更新施法时间,更新 w[ i ]。
w[ i ]:记录p[ i ]之前 i 的魔法加成
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,k,day,op,x,f[N],g[N],h[N];
void over(int x){
if(g[x]==0){
return ;
}
if(f[x]+g[x]-1<=day){
h[x]+=g[x];
g[x]=0;
}
else{
int tmp=f[x]+g[x]-1-day;
h[x]+=day-f[x]+1;
g[x]=tmp;f[x]=day+1;
}
}
int main(){
cin>>n>>m>>k;
while(m--){
cin>>op>>x;
if(op==1){
over(x);
f[x]=day+1;
g[x]=k;
}
else if(op==2){
over(x);
g[x]=0;
}
else{
over(x);
cout<<h[x]+day<<"\n";
}
day++;
}
return 0;
}
T3
题意
有n只兔子,每只兔子都有可爱值和祝福值,你要求在祝福值不超过 H 且可爱值之和为7的倍数时,可爱值之和最大是多少
思路
01背包增加限制:挑选可爱值和为7的倍数。
设 f(i , j , k) :表示前 i 个物品,空间剩余 j ,价值 %7 为 k 。也就是在 01背包 的基础上多一层关于 k 的转移。
但是本题需要注意初始化,例如: f(0,0,1) 是一定不合法的,需要初始化为负无穷,其余类似情况都需要清空。
状态转移方程:f [ i ][ j ][ k ] = max( f[ i-1 ][ j ][ k ],f[ i-1 ][ j-b[i] ][ k-a[i] ])
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,h,a[10005],b[10005],f[2][7],dp[2][1005][7];
signed main(){
cin>>n>>h;
for(int i=1;i<=n;++i){
cin>>a[i];
}
for(int i=1;i<=n;++i){
cin>>b[i];
}
if(h==998244353){
memset(f,-0x3f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=n;++i){
int t=i&1;
for(int j=0;j<7;++j){
f[t][j]=max(f[t^1][j],f[t^1][((j-a[i])%7+7)%7]+a[i]);
}
}
cout<<f[n&1][0];
return 0;
}
memset(dp,-0x3f,sizeof(dp));
dp[0][0][0]=0;
for(int i=1;i<=n;++i){
int t=i&1;
for(int j=0;j<=h;++j){
for(int k=0;k<7;k++){
if(j<b[i]){
dp[t][j][k]=dp[t^1][j][k];
}
else{
dp[t][j][k]=max(dp[t^1][j][k],dp[t^1][j-b[i]][((k-a[i])%7+7)%7]+a[i]);
}
}
}
}
int ans=0;
for(int i=0;i<=h;++i){
ans=max(ans,dp[n&1][i][0]);
}
cout<<ans;
return 0;
}
T4
题意
作为一颗成熟的奥术飞弹,不管当前处于哪个房间,都会瞬间规划好 一条 前往目标所在位置的最短飞行弹道(当然,最短飞行弹道有时候并不是唯一的,所以有多条最短飞行弹道时会随机选择一条),如果它没有沿着当前房间规划好的最短飞行弹道飞行,记为偏离轨迹1次。
寻找出所有的最短飞行弹道,每条最短飞行弹道都有一个可能偏离数,找到其中最大的可能偏离数,输出最短飞行弹道条数和最大的可能偏离数。
简而言之,就是 n 个点的无向连通图,无重边和自环,目标是从1点到达 n 点。在一个点上,如果有多条最短路径到达终点,则可能偏离数加一,求出最短路径的总条数,以及所有最短路中可能偏离数的最大值。
思路
最短路计数问题。
第一次广搜,记录出来 dis 数组, 表示点 i 到 n 的最短路长度
第二次广搜,从 n 为起点,宽搜每个点,可以求出 n 到每个点的最短路,同时记 fa(i)表示 i 的父亲(宽搜时的前继点)。
得出结论:从 x 到 y 的边在最短路可以看作从 x 的最短距离等于 y 的最短距离 +1。
最终要求从 1 到 n 的最短路计数,可以离 n 从近到远的考虑,枚举( x ,y),如果dis[ y ]+1=dis[ z ],说明( x ,y)在最短路边上,情况进行累加,cnt[ x ]+=cnt[ y ]
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5,mod=998244353;
struct node{
int to,nxt;
}edge[N<<2];
int n,m,cnt=1,head[N],hd=1,tail,q[N],dis[N],pre[N],tot[N],ans[N];
void add(int x,int y){
edge[cnt].to=y;
edge[cnt].nxt=head[x];
head[x]=cnt++;
}
void bfs(){
memset(dis,-1,sizeof dis);
dis[n]=0;
q[++tail]=n;
while(hd<=tail){
int u=q[hd++];
for(int i=head[u];i;i=edge[i].nxt){
int t=edge[i].to;
if(dis[t]==-1){
dis[t]=dis[u]+1;
pre[t]=u;
q[++tail]=t;
}
}
}
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
while(m--){
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
bfs();
tot[n]=1;
for(int i=1;i<=tail;i++){
int u=q[i],flag=0;
for(int j=head[u];j;j=edge[j].nxt){
int v=edge[j].to;
if(dis[u]==dis[v]+1){
ans[u]=max(ans[u],ans[v]);
(tot[u]+=tot[v])%=mod;
if(v!=pre[u]) flag=1;
}
}
ans[u]+=flag;
}
cout<<tot[1]<<" "<<ans[1];
return 0;
}