[USACO 2022 Mar Bronze P3] Alchemy题解

46 篇文章 2 订阅

前言

在前几天我做了USACO3月赛,今天打算将题解写给大家看。

题目

链接

查看题目

题面

039:[USACO 2022 Mar Bronze P3] Alchemy
查看提交统计提问
总时间限制: 20000ms 单个测试点时间限制: 1000ms 内存限制: 262144kB
描述
总是热衷于培养新的爱好的奶牛 Bessie 正在学习如何转化金属。对于 1≤i≤N≤100,她有 ai(0≤ai≤104)单位的金属 i。此外,她知道 K(1≤K<N)个配方,她可以融合若干种金属各一单位,制造一单位编号大于所有被融合金属的金属。另外保证,对于每种金属,Bessie 最多知道一种制造该金属的配方。

计算经过一系列转化后,Bessie 可能拥有的金属 N 的最大单位数。

输入
输入的第一行包含 N。
第二行包含 N 个整数 ai。
第三行包含 K。
以下 K 行每行包含两个整数 L and M(M≥1),随后是 M 个整数。后 M 个整数表示配方中用于制造一单位金属 L 所需要被融合的金属。输入保证 L 大于这 M 个数。

测试点 2 中,对于 1≤i测试点 3-4 中,每个配方均将一单位的一种金属转化为另一种金属。
测试点 5-11 没有额外限制。
输出
输出在应用一系列零次或多次转化后,Bessie 可能拥有的金属 N 的最大单位数。
样例输入
5
2 0 0 1 0
3
5 2 3 4
2 1 1
3 1 2
样例输出
1
提示
样例说明:
在这个例子中,以下是一种最优的转化方式:

将一单位金属 1 转化为金属 2。
将一单位金属 2 转化为金属 3。
将一单位金属 3 和金属 4 转化为金属 5。

现在 Bessie 还有一单位金属 1 和一单位金属 5。她无法再制造更多的金属 5。
来源
USACO 2022 Mar Bronze P3
//题目数据为作者老师所写(北京大学计算机算法系学生),比USACO数据强度还高!!!

分析

难度评估

算法思维:CSP-J中等
代码实现难度:CSP-J难
依据取决于作者的C++老师,数据权威!!!

看到这道题,学过一点点算法的人肯定想的是递归,哈哈TLE安排,还是来看正解吧。

枚举方法

解析

  • 这道题可以相当于最上级依次借1个元素,如果不够就将配方里的其他下级元素借1,依次类推。

  • 当开始新的一轮时,从n到1访问,如果发现i为-x时,我们首先判断有没有i的配方(题目说过每个金属的配方只有一个)和有没有符合一个重要的点(后面说道),如果都符合将i的配方中的元素都-x,以此类推;不符合则本轮结束

  • 具体过程如图:详细过程

代码

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e2+10;
int n,k,ans,flag=1;
int a[maxn];
bool vis[maxn];
vector<int>e[maxn]; 
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	scanf("%d",&k);
	//数据存储
	while(k--){
		int op,m,x;
		scanf("%d%d",&op,&m);
		vis[op]=true;
		while(m--){
			scanf("%d",&x);
			e[op].push_back(x);
		}
	}
	//层层访问
	while(1){
		//新一轮开始
		a[n]--;
		if(a[n]<0){//判断a[n]能否支持元素数量+1
			//循环判断
			for(int i=n;i>=1;i--){
				//如果为负数,需要向下级访问
				if(a[i]<0){
					//判断是否符合要求
					if(!vis[i]||a[i]<-(i-1)*10000){
						flag=0;
						break;
					}
					//下级处理
					for(int j=0;j<e[i].size();j++){
						a[e[i][j]]+=a[i];
					} 
					a[i]=0;
				}	
			}
		}
		if(!flag)break;
		//更新答案
		ans++;
	}
	printf("%d\n",ans);
	return 0;
} 

代码评估及注释

此题用枚举方法的时间复杂度是O(ans*n^2)~1e8->1e9,没点代码的基础很容易TLE(Time Limited Exceeded超时),本人针对代码的几个关键地方进行注释。

注释:

  1. 需使用邻接表存储,用邻接矩阵存储会导致不必要的时间复杂度;
  2. 在这一条指令中包括了两种限制if(!vis[i]||a[i]<-(i-1)*10000)
    1. !vis[i] 表示金属i没有配方。
    2. a[i]<-(i-1)*10000表示比他角标小的金属数量的总和都比他所需要的还少,证明本轮无解,注意a[i]最快速度为几何数增长,总结为此指令为了将时间复杂度降低。

后继

到这里枚举方式的代码就写完了,相信a[i]<-(i-1)*10000这条指令,大佬基本上是能写出来的,所以本人又用了另一种方式来解决。

二分方法

解析

沿着上一题的思路,我们可以想到二分最终答案的方法(大众应用),怎么二分呢?需要什么条件呢?

  • 首先思考2个questions:
    1. 最终答案最少是多少?
    2. 最终答案最多是多少?
  • solve questions:
    1. 太简单了,最终答案最少是0。
    2. 我们可以思考最极端的情况是金属i+1的配方只有金属i(1<=i<n),这样可以推出金属n的最终数量为1e4n(~1e6),所以最终答案最多是1e6;

思考完了上代码。

代码

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e2+10;
const int I=1e6;
int n,k,ans,flag=1,l=0,r=I;
int a[maxn],b[maxn];
bool vis[maxn];
vector<int>e[maxn];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	scanf("%d",&k);
	//初始化数据
	while(k--){
		int op,m,x;
		scanf("%d%d",&op,&m);
		vis[op]=true;
		while(m--){
			scanf("%d",&x);
			e[op].push_back(x);
		}
	}
	//二分答案
	while(l<=r){
		flag=1; 
		int mid=(l+r)/2;
		for(int i=1;i<=n;i++)b[i]=a[i];
		b[n]-=mid;
		//枚举方法相同思路
		if(b[n]<0){
			for(int i=n;i>=1;i--){
				if(b[i]<0){
					if(!vis[i]||b[i]<-(i-1)*10000){
						flag=0;
						break;
					}
					for(int j=0;j<e[i].size();j++)b[e[i][j]]+=b[i];
					b[i]=0;
				}
			}
		}
		if(!flag)r=mid-1;
		else {
			ans=mid;
			l=mid+1;
		}
	}
	printf("%d\n",ans);
	return 0;
} 

灌水

到这里博客就写完了,感谢大家观看,大家有什么意见都可以说,不管是什么人,只要你说的对,对我语文表达和代码提升有帮助我就改正。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
USACO2022金组是国际在线判题系统USACO的最高级别,题目难度较高,在该比赛中取得好成绩是一项巨大的成就。以下是对该比赛的一些题目解析。 第一题:“交通计划” 题目要求:给定一个n个节点的有向图,每条边有一个长度,希望添加最少的边使得所有节点连通,求最小生成树的权值和。 解析:该题可以使用Kruskal算法求解,将每条边按权值从小到大排序,再依次加入,判断加入的边是否会形成环,若形成则不加入,直到所有节点连通为止。此时Kruskal算法得到的最小生成树的权值和即为所求。 第二题:“点火计划” 题目要求:给定一个n个节点的有向图,每条边有一个权值和一个点火时长,每个节点有一个点火启动时刻和时刻结束时刻,希望从其中选出一些边点火,使得所有节点都可从点火的边出发到达,且所选点火边的总点火时长最小。 解析:该题可以使用最小费用最大流算法求解。将每条边看做一个容量为1,费用为点火时长的边,源点向节点的点火边容量为1,费用为0的边,节点的点火边向汇点的容量为1,费用为0的边,对这个网络进行最小费用最大流即可得到所选边的总点火时长最小。 第三题:“美味佳肴” 题目要求:给定n个菜品,每个菜品有它的权值和两个类别,希望选出k个菜品,使得选出的菜品数量在每个类别中都不超过$\frac{k}{3}$个,且所选菜品的权值和最大。 解析:该题可以使用动态规划求解。设$f[i][j][k]$表示前i个菜品中,选择j个一类菜品,选择k个二类菜品的最大权值和,状态转移方程为$f[i][j][k]=max(f[i-1][j][k],f[i-1][j-1][k]+a[i],f[i-1][j][k-1]+b[i])$,其中a[i]为i号菜品的权值,若为一类则为该权值,否则为0,b[i]为i号菜品的权值,若为二类则为该权值,否则为0。最终答案为$f[n][$k/3$][$k/3$]。 以上是对USACO2022金组的部分题目的解析,USACO比赛是全球范围内的计算机竞赛,竞争非常激烈,能够在该比赛中脱颖而出是一项非常棒的成就。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

materialistOier

我只是一名ssfoier

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值