SDUT 2023 summer team contest(for 22) - 9补题和总结
C - Association for Control Over Minds
题面:
“我今天只能制作其中一些药剂,其余的以后再做。” 你决定道。你按照编号从1到N的顺序逐个考虑你所有的配方。对于每个配方,如果你无法调制这个药剂(下一段解释),你跳过这个配方,考虑下一个,如果有的话。否则,即使这可能导致一些接下来的药剂无法调制,你也会调制这个配方。因此,是否调制一个药剂并不是一个选择,它只取决于你考虑药剂时是否可能进行调制。
为了调制一个药剂,首先你准备一个新的空锅(你从杂货店里弄到了无限多的锅)。然后,在这个锅中,你只把所需的所有成分放进去,不能有任何不在配方中列出的成分(也就是说,锅里不能含有任何未在配方中列出的成分)。对于那些之前你决定调制的药剂中没有使用过的成分,你可以简单地将其放入这个锅中。你还可以将之前用过的锅中的所有内容倒入这个锅中,以便混合其中的所有成分(由于你倒入了锅中的全部内容,这个旧的调制物将无法再用于下一次调制)。最后,你用扫帚搅拌这个锅,并取出一瓶混合物稍后用于测试你的手下。剩下的混合物仍然留在这个锅中,可以稍后倒入另一个锅中。
“今天你要调制多少个配方?” 你问自己。
输入
第一行包含一个非负整数2≤N≤200,000,表示你拥有的总配方数。接下来是N行,第i行描述第i个配方。每行是一个以空格分隔的整数列表。每行以一个整数1≤M开头,表示制作该配方所需的原料数量。然后,接下来是M个整数,描述所需的原料。原料通过0到500,000之间的整数进行标识,不同的整数代表不同的原料。对于每个配方,所有的原料都是不同的。所有配方中所需原料数量的总和不会超过500,000。
输出
打印出你将要调制的配方的数量。
样例数据说明
在第一个示例中,第一个药剂可以调制,因为两种原料都没有被使用过。因此,你将调制这个药剂。由于同样的原因,第二个药剂也可以调制。第三个药剂无法调制,因为原料1已经不存在,并且与另一种不在配方中的成分混合在一起。通过倒入用于第一次和第二次调制的锅中的内容,并添加到目前未使用过的原料5,第四个药剂可以调制。最后一个药剂无法调制,因为第一次调制的锅中的内容已经全部倒入了第四个药剂中,现在混合了其他不在配方中的成分。
对于第二个示例,由于第一个药剂可以调制,它必须被调制。因此,第二个和第三个药剂无法再被调制。
题意:利用并查集把每次完成的材料存到这次用到的编号最小的材料那里,并记录好以这个材料开头的并查集的集合元素个数。转移的时候把个数转移过去就行.
对于每一个东西,先利用并查集把他们变成他们所处集合的集合首,去掉重复元素,然后再加和各个集合元素个数,如果结果恰好是材料个数,就能够完成这个东西
#include <bits/stdc++.h> using namespace std; #define N 500002 int fa[N],num[N]; int findfa(int x) { return fa[x]==x?x:findfa(fa[x]); } int main() { int n,m,ans; while(cin>>n) { ans=0; for(int i=0;i<=N;i++) num[i]=1,fa[i]=i; while(n--) { scanf("%d",&m); vector<int> a(m,0); int sum=0; for(int i=0;i<m;i++) { scanf("%d",&a[i]); a[i]=findfa(a[i]+1); } sort(a.begin(),a.end()); a.erase(unique(a.begin(),a.end()),a.end()); for(int i=0;i<a.size();i++) sum+=num[a[i]]; if (sum==m) { ans++; int x=a[0],y; for(int i=1;i<a.size();i++) { y=a[i]; fa[y]=x; num[x]+=num[y]; num[y]=0; } } } printf("%d\n",ans); } return 0; }
E - Association for Computing Machinery
E题这个情景很符合现实,我的队友在看完这个题面之后才第一次理解了icpc赛制的排名方式,首先给出n和p,分别代表题目数量和从第几道题开始做,接下来一行n个数据是n道题目所用时间最后输出做出来的题目总数和总罚时
#include<bits/stdc++.h> using namespace std; #define int long long void liuwansi() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); } signed main() { liuwansi; int n,p; while(cin>>n>>p) { int a[n]; int tm=0,cnt=0; for(int i=0;i<n;i++)cin>>a[i]; if(a[p]<=300) { swap(a[0],a[p]); sort(a+1,a+n); for(int i=1;i<n;i++)a[i]+=a[i-1]; for(int i=0;i<n;i++) { if(a[i]<=300) { tm+=a[i]; cnt++; }else if(a[i]>300)break; } } cout<<cnt<<' '<<tm<<endl; } }
G - Association for the Country of Mububa
一道有些困难的dp题,一开始伪装成了贪心题的模样,具体的题目题意是有n个数,要按顺序将其分成m组,后一组数据之和要大于等于前一组数据之和,m要尽可能的大;
一开始我会考虑使用贪心来进行计算比如这样的代码:
#include<bits/stdc++.h> #include<stdio.h> #include<string.h> #include<cstdlib> #include<iostream> using namespace std; int x,y,z,i,j,sum; long long int a[5000],m,n; int main() { scanf("%d",&x); for(z=1; z<=x; z++) scanf("%lld",&a[z]); m=a[1]; n=0; sum=1; for(z=2; z<=x; z++) { n+=a[z]; if(m<=n) { sum++; m=n; n=0; } } cout<<sum<<endl; return 0; }
但是这时候贪心法的弊端就显现出来了,比如
5 3 3 1 4 4
会输出3,即:3 3 1+4 4(没用舍去)
但实际上应该是 3 3+1 4 4 (共4个)
所以应该写一个dp:
#include<bits/stdc++.h> using namespace std; #define int long long signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int T=1; while(T--) { int n; cin>>n; vector<int> a(n+1),s(n+1),dp(n+1),b(n+1); for(int i=1;i<=n;i++) { cin>>a[i]; s[i]=s[i-1]+a[i]; } for(int i=1;i<=n;i++) { for(int j=i-1;j>=0;j--) { if(s[i]-s[j]>=b[j])//b表示i结尾的段的最小情况 { //对于每个i从大到小找到第一个j,使得s[j+1 ~ i] //恰好大于b[j] //此时b[i]就是s(j+1~i)dp[i]=dp[j]+1 b[i]=s[i]-s[j]; dp[i]=dp[j]+1; break; } } } cout<<dp[n]<<endl; } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)