Day5补题报告

姓名:徐浩轩,校区:和谐校区,考试时间: 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值