2020年广东工业大学第十届文远知行杯新生程序设计竞赛(同步赛)
以下是2020年广东工业大学第十届文远知行杯新生程序设计竞赛(同步赛)个人整理:
Problem A 肥猪的钢琴床
答案解释:
假设0^a 表示连续a个0,1^a表示连续a个1
通过观察容易发现答案是0^a 1^b 0^c
形式的(当然abc可能为0),此时一个简单的想法就是枚举两个分界点
,但事实上在确定了左端点之后右端点是很容易通过预处理找到的,我们可以假设枚举完左端点之后先将左端点左边全部转为0
,右端点全部转为1
,此时我们想要知道的是0和1出现次数差值最大的右端点,那么我们是需要事先预处理后缀0和1出现次数差
的最大值就可以了。
给出相对容易理解的代码:
#include<bits/stdc++.h>
using namespace std;
char a[1000005];
int main()
{
int n;
scanf("%d",&n);
scanf("%s",a);
int sum=0,maxx=0,ans=0;
for(int i=0;i<n;i++)
{
if(a[i]=='1') sum++;
}
for(int i=0;i<n;i++)
{
if(a[i]=='1') ans++;
else if(a[i]=='0') ans--;
if(ans<0) ans=0;
if(ans>maxx) maxx=ans;
}
printf("%d",sum-maxx);
return 0;
}
这里引用一个其他的思路:
我们发现对于每个点来说,都有两种情况,一种是取1留0,一种是取0留1,这正好是吻合了dp的想法,还有一种极端情况是把前面的所有的1全部去掉。所以我们就获得了三个dp式子
dp[i][0]=dp[i-1][0]+a[i]-‘0’;
dp[i][1]=min(dp[i-1][1]+‘1’-a[i],dp[i-1][0]+‘1’-a[i]);
dp[i][2]=min(dp[i-1][1]+a[i]-‘0’,dp[i-1][2]+a[i]-‘0’);
最后三种情况取大就好了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char a[1000007];
int dp[1000007][5];
int main()
{
int n;
scanf("%d",&n);
scanf("%s",a+1);
for(int i=1;i<=n;i++){
dp[i][0]=dp[i-1][0]+a[i]-'0';
dp[i][1]=min(dp[i-1][1]+'1'-a[i],dp[i-1][0]+'1'-a[i]);
dp[i][2]=min(dp[i-1][1]+a[i]-'0',dp[i-1][2]+a[i]-'0');
}
printf("%d\n",min(dp[n][0],min(dp[n][1],dp[n][2])));
return 0;
}
Problem B 拯救小a
本场的签到题~考察新生字符串处理的能力
(答案不唯一)
#include<bits/stdc++.h>
using namespace std;
int cnt = 0;
int main(){
string s;
while(cin>>s && s[0]!='.'){
for(int i=0;i<=s.size();i++){
if(s[i] == 'a') cnt++;
}
}
printf("%d",cnt);
return 0;
}
Problem C 母牛的俄罗斯轮盘赌
答案解释:
显然能开出第n-1枪的是胜者,易证得开出第k枪的玩家一定能开出第k+5枪,算出n<=5的答案即可。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
if( n%5==0 || n%5==2 ) printf("Cow\n");
else printf("Pig\n");
}
return 0;
}
每一个n都与它前面的n有关联,所以列表可发现规律。
1 2 3 4 5 6 7 8 9 10 ...
P C P P C P C p p c ...
Problem D 中学数学题
答案解释:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--){
int n,p,sum = 0;
cin>>n>>p;
while(n){
sum += n/p;
n/=p;
}
cout<<sum<<endl;
}
return 0;
}
Problem E 枚举求和
答案解释:
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
long long m,n,k;
cin>>t;
while(t--)
{
cin>>n>>m>>k;
cout<<(n/k)*(m/k)<<endl;
}
return 0;
}
Problem F 合并石子
答案解释:
你只关心在所有合并方案当中,某一堆石子会被合并多少次。
f[i][j]表示还剩i堆石子,当前第j堆,在后续所有合并方案中被合并的次数。
转移O(1)
#include<bits/stdc++.h>
#define N 2010
#define mod 1000000007
using namespace std;
typedef long long ll;
ll f[N],d[N];
int main(){
f[0]=1;
for(ll i=1;i<N;i++)
f[i]=f[i-1]*i%mod;
for(ll i=2;i<N;i++)
d[i]=(f[i-1]*2%mod+i*d[i-1]%mod)%mod;
int n;
cin>>n;
cout<<d[n]<<endl;
return 0;
}
Problem G 排解忧伤
答案解释:
显然,无论以任何顺序入场,怒气值之和都不会改变。
你只要以你喜欢的顺序入场计算就好了。
#include<bits/stdc++.h>
using namespace std;
long long a[100005],ans = 0;
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
a[x]++;
}
for(int i=1;i<n;i++)
{
while(a[i]>1)
{
a[i+1] += (a[i]-1);
ans += (a[i]-1);
a[i] = 1;
}
}
if(a[n]>1)
cout<<"-1"<<endl;
else
cout<<ans<<endl;
return 0;
}
Problem H 台灯
答案解释:
Problem I 历史
答案解释:
本场的签到题~因为是新生赛所以希望可以通过这一道题目让新生们了解一下acm的历史以及基本规则。
#include<iostream>
using namespace std;
int main(){
cout<<"The Chinese teams has won 4 championships";
return 0;
}
Problem J 母牛烃
答案解释:
显然双键的能量在1至n-1之间。
考虑从高到低依次满足能量,并且每次满足能量时,选取的势力值都是未选择势力值的最值。
可以证明如果每次都遵循这个原则,最后一定能构造出一个合法方案。
定义在一个碳原子上添加双键称为扩展。
对于能量n-1,必定为1与n相连产生。
对于能量n-2,可由1与n−1相连或2与n相连产生。
假设在满足能量n-1时选择前者,则会产生(n-1)-1-n的链;若选择在n上扩展,则会导致势力值22被忽略,违背了上诉原则。同理若选择了后者,则在11上扩展时,同样会违背上诉规则。
因此,归纳可知每次扩展之间必定存在一个公共碳原子,且只要满足存在公共碳原子,则可得到合法方案。
基于此,只需要在主链上扩展时始终满足存在公共碳原子即可得到一组合法方案。
如下图:
#include<stdio.h>
int a[10000],b[10000];
int main(void){
int x,m,n,i,j,k,l,r,ans=0;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++) scanf("%d",&a[i]);
b[a[1]]=n;
l=1;
r=n-1;
scanf("%d",&x);
for(i=2;i<m;i++){
scanf("%d",&k);
if(i%2==0){
b[a[i]]=l;
l++;
for(j=1;j<=k;j++){
scanf("%d",&x);
b[x]=r;
r--;
}
}
else{
b[a[i]]=r;
r--;
for(j=1;j<=k;j++){
scanf("%d",&x);
b[x]=l;
l++;
}
}
}
scanf("%d",&x);
b[a[m]]=l;
for(i=1;i<=n;i++){
if(i==n) printf("%d",b[i]);
else printf("%d ",b[i]);
}
}
Problem K 很基础的模拟题
答案解释:
签到模拟题
#include <bits/stdc++.h>
using namespace std;
int main(){
int a[105];
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
cin>>a[i];
}
while(q--){
int func = 0;
scanf("%d",&func);
if(func == 1){
int x;
scanf("%d",&x);
for(int i=x+1;i<=n;i++){
a[i-1] = a[i];
}
n--;
for(int i=1;i<=n;i++){
printf("%d ",a[i]);
}
cout<<endl;
}
else if(func == 2){
int x,y;
scanf("%d%d",&x,&y);
for(int i=n+1;i>x;i--){
a[i] = a[i-1];
}
n++;
a[x] = y;
for(int i=1;i<=n;i++){
printf("%d ",a[i]);
}
cout<<endl;
}
else{
int x,cnt = 0;
scanf("%d",&x);
for(int i=x+1;i<=n;i++){
if(a[i] == a[x]){
cnt++;
}
else{
break;
}
}
a[x] = a[x] + cnt*a[x];
for(int i=x+1;i<=n-cnt;i++){
a[i] = a[cnt+i];
}
n -= cnt;
for(int i=1;i<=n;i++){
printf("%d ",a[i]);
}
cout<<endl;
}
}
return 0;
}
Problem L 母牛上柱
答案解释:
签到数学题。两个方向都计算一遍取最小即可。
#include<bits/stdc++.h>
#define PI 3.1415926535
using namespace std;
int main(){
int t;
cin>>t;
for(int i=1;i<=t;i++){
int a,b,r,h;
cin>>a>>b>>r>>h;
int aa = abs(b-a) >180 ? 360-abs(b-a) : abs(b-a);
if(aa == 0){
double ans = h*h;
printf("%.2lf\n",ans);
}
else{
double ans,l;
l = 2*PI*r*aa/360;
ans = h*h+l*l;
printf("%.2lf\n",ans);
}
}
return 0;
}
小伙伴们,强烈安利一波👇:
推荐一款轻便实用的Chrome插件(限时免费下载,赠去广告特权)
以上属个人见解。
❤️希望对您有帮助,您的支持是我创作最大的动力!
推荐你可能喜欢的其他精彩内容:
CDN是什么?一分钟带你了解CDN
【Linux学习】CentOS6.8安装步骤的详细教程
第十届蓝桥杯大赛软件类省赛C/C++大学B组 题解
Windows10下利用DOSBOX和MASM32搭建汇编语言环境
最新Chromedriver与Chrome版本对应参照表【附下载链接】