2020年广东工业大学第十届文远知行杯新生程序设计竞赛题解
A. 肥猪的钢琴床
题意:
将 01字符串转化为最多只有一段连续1的字符串,求最小删除字符数
思路:
转化后的字符串可化为 \(0 ⋯ 0 + 1 ⋯ 1 + 0 ⋯ 0\)三段,每一段的长度都可能为 0
记 \(dp[i][0/1/2]\)为第 \(i\)个字符位于 \(1 / 2 / 3\) 段需要删除的最小字符(第 \(i\)个字符可能要被删除)
状态转移方程很简单,详见代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
char s[N];
int n,dp[N][3];
int main() {
scanf("%d%s",&n,s+1);
for(int i=1;i<=n;i++) {
if(s[i]=='1') {
dp[i][0]=dp[i-1][0]+1;//把1删了
dp[i][1]=min(dp[i-1][0],dp[i-1][1]);//第二段的1,可能在第二段首,也可能不在
dp[i][2]=min(dp[i-1][1],dp[i-1][2])+1;//第三段的0,可能在出现在第三段首,也可能不在,但一定被删
}
else{
dp[i][0]=dp[i-1][0];
dp[i][1]=min(dp[i-1][0],dp[i-1][1])+1;//第二段的0,可能在出现在第二段首,也可能不在,但一定被删
dp[i][2]=min(dp[i-1][1],dp[i-1][2]);//第三段的0,可能在出现在第三段首,也可能不在
}
}
//最后一个字符一定处于这三种状态中,挑出最小的即可
printf("%d\n",min({dp[n][0],dp[n][1],dp[n][2]}));
return 0;
}
也可写为:
scanf("%s",s+1);
for(int i=1;i<=n;i++){
dp[i][0] = dp[i-1][0] + (s[i]-'0');
dp[i][1] = min(dp[i-1][0] + ('1'-s[i]), dp[i-1][1] + ('1'-s[i]));
dp[i][2] = min(dp[i-1][1] + (s[i]-'0'), dp[i-1][2] + (s[i]-'0'));
}
cout<<min(dp[n][0], min(dp[n][1], dp[n][2]));
B:签到
C. 母牛的俄罗斯轮盘赌
思路:
简单博弈:
- 打表到10发现周期为5重复(博弈有时要耐心打表)
- 显然能开出第n-1枪的是胜者,易证得开出第k枪的玩家一定能开出第k+5枪,算出n<=5的答案即可
D. 中学数学题
思路:
数论:
原题\(\Leftrightarrow\)找到阶乘中能除尽p的因子\(\Leftrightarrow\)再求这些因子能除尽p的次数总和
#include <stdio.h>
int main(){
int t;
scanf("%d",&t);
int n,p;
for(int i = 0;i < t;i++){
scanf("%d%d",&n,&p);
int count = 0;
for(int j = p;j <= n;j += p){ //找出所有p的倍数:1p,2p,3p...
int k = j;
while(k % p == 0){//计算p的倍数中包含p个数,即要除以几次才能余数不为0,即0的个数
count++;
k /= p;
}
}
printf("%d\n",count);
}
return 0;
}
E. 枚举求和
思路: 有多少个\(i\)和\(j\)都是\(k\)的倍数,于是,直接输出\((n/k)*(m/k)\).
F. 合并石子
思路:
\[消耗体力总和=合并时的所有情况数*\sum ^{n-1}_{i=1}第i次合并时任意选取两堆的期望和
\]

#include <bits/stdc++.h>
#define M 1000000007
using namespace std;
typedef long long ll;
ll ksm(ll a,ll p){
ll res=1;
while(p){
if(p&1){res=res*a%M;}
a=a*a%M;p>>=1;
}
return res;
}
int i;
ll n,res,tmp;
int main(){
scanf("%d",&n);
for(i=2;i<=n;i++)
res+=ksm(i,M-2)*n%M;//对i求模M的逆元
for(i=1;i<=n-1;i++)
res=res*i%M;
printf("%lld",res*2%M);
}
G. 排解忧伤
思路:
显然,无论以任何顺序入场,怒气值之和都不会改变,你只要以你喜欢的顺序入场计算就好了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>inline void read(T &x){
x=0;register char c=getchar();register bool f=0;
while(!isdigit(c))f^=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(f)x=-x;
}
const int N=1e5+5;
ll n,m,s=1,ans=0,a[N];
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++) read(a[i]);
sort(a+1,a+1+m);
for(int i=1;i<=n;i++){
//刚开始a[s]==i时就坐下,之后可能就a[s]<i时坐下
if(a[s]<=i){
ans+=i-a[s];
s++;
}
if(s==m+1) break;
}
if(s!=m+1) puts("-1");
else cout<<ans;
return 0;
}
I. 历史:签到
K. 很基础的模拟题:复习vector啦
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include<bits/stdc++.h>
#define LL long long
using namespace std;
template<class T>inline void read(T &x){
x=0;register char c=getchar();register bool f=0;
while(!isdigit(c))f^=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(f)x=-x;
}
int main(){
vector<int> v;
int n,q,t;
cin>>n>>q;
int q1;
int c1,c2;
for(int i=0;i<n;i++)read(t),v.push_back(t);
while(q--){
cin>>q1;
if(q1==1){
cin>>c1;
v.erase(v.begin()+c1-1);
for(auto it:v)
cout<<it<<" ";
cout<<endl;
}
else if(q1==2){
cin>>c1>>c2;
v.insert(v.begin()+c1-1,c2);
for(auto it:v)
cout<<it<<" ";
cout<<endl;
}
else{
cin>>c1;
c1--;
int k=*(v.begin()+c1),cnt=1;
vector<int>::iterator it;
int sum=k;
for(it=v.begin()+c1+1;it!=v.end();it++){
if(*it==k)
sum+=k,cnt++;
else break;
}
for(int i=0;i<cnt;i++)
v.erase(v.begin()+c1);
v.insert(v.begin()+c1,sum);
for(auto it:v)
cout<<it<<" ";
cout<<endl;
}
}
return 0;
}
L. 母牛上柱
思路:
签到,注意angle=fabs(a-b); angle=min(angle,360-angle),几何关系WA了一发,血亏

浙公网安备 33010602011771号