哈尔滨理工大学软件与微电子学院程序设计竞赛(同步赛)赛后总结
20年11月27日的比赛了,一共20题,当时写了十七题rank30,现在拿出来把它整理一下吧。
A找一下规律。前三行:第i行输出i-1个空格和V和2*(n-i)-1个空格和V和换行,最后一行输出3个空格和一个V,循环搞定。
#include<bits/stdc++.h> using namespace std; int main() { int n=4; for(int i=1;i<n;i++) { for(int j=1;j<i;j++)cout<<' '; cout<<'V'; for(int j=1;j<=1+2*(n-i-1);j++) cout<<' '; cout<<'V'; cout<<endl; } for(int j=1;j<=n-1;j++) cout<<' '; cout<<'V'; return 0; }
B就读入ab,输出100*b/a的三位小数+百分号,用printf的保留三位小数挺好。(还好不想yyh的那道毒瘤题)
#include<bits/stdc++.h> using namespace std; int main() { long long a,b; cin>>a>>b; double x=100.00*b/a; printf("%.3f%%",x); return 0; }
C就读入mn,如过m%n==0输出YES,否则输出NO。
#include<bits/stdc++.h> using namespace std; long long m,n; int main() { cin>>m>>n; if(m%n==0) cout<<"YES"; else cout<<"NO"; return 0; }
D题可以看做是一道60进制的减法。我就直接sum=(h2-h1)*60+m2-m1得到总的分钟数,那么小时应该是sum/60,分钟数应该是sum%60。想一秒特殊情况:不到一小时和整小时,都挺对的,那就没啥问题了。
#include<bits/stdc++.h> using namespace std; int a,b,c,d; int main() { cin>>a>>b>>c>>d; int sum=d-b+(c-a)*60; cout<<sum/60<<' '<<sum%60; return 0; }
E也是小模拟。本来还想开数组啥的,看一眼数据范围是11~99仅有两位数,if表达式挺好写的,直接开搞。a==b时100块;20块是12、21这样个位对应十位,十位对应个位这样,a/10==b%10&&a%10==b/10;2块是有一个数字相同,a%10==b/10||a%10==b%10||a/10==b/10||a/10==b%10。上面的用if和else if,0块用else即可对应全部情况。
#include<bits/stdc++.h> using namespace std; int a,b; int main() { cin>>a>>b; if(a==b) cout<<100; else if(a%10==b/10&&a/10==b%10) cout<<20; else if(a%10==b/10||a%10==b%10||a/10==b/10||a/10==b%10) cout<<2; else cout<<0; return 0; }
F不想说了。
#include<bits/stdc++.h> using namespace std; int main() { int n; for(cin>>n;n;n--) cout<<"China will win the battle against COVID-19."<<endl; return 0; }
G可以sort得到最高最低分,但是平均分还是要扫一遍,那顺便更新最高分最低分算了,不用sort了。
#include<bits/stdc++.h> using namespace std; int a[10010],maxx,minn,sum; int main() { int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; maxx=minn=a[1]; for(int i=1;i<=n;i++) { maxx=max(maxx,a[i]); minn=min(minn,a[i]); sum+=a[i]; } printf("%.2f %d %d",sum*1.0/n,maxx,minn); return 0; }
H到这儿我想起来了,我是先做的H再做的A,于是可以照搬代码到A。那些先写A的还要推一下公式哈哈哈哈。
#include<bits/stdc++.h> using namespace std; int main() { int n;cin>>n; for(int i=1;i<n;i++) { for(int j=1;j<i;j++)cout<<' '; cout<<'V'; for(int j=1;j<=1+2*(n-i-1);j++) cout<<' '; cout<<'V'<<endl; } for(int j=1;j<=n-1;j++) cout<<' '; cout<<'V'; return 0; }
I用map和数组写都挺好。map写完还要用迭代器,那个我没学会,还是算了。这波啊,这波是空间换时间,如果空间开不下要用nlog(n)sort后扫一遍,写起来也简单。
#include<bits/stdc++.h> using namespace std; int t,sum[10010]; int main() { int n; cin>>n; for(int i=1;i<=n;i++) { cin>>t; sum[t]++; } for(int i=10000;i;i--) if(sum[i]) cout<<i<<'-'<<sum[i]<<endl; return 0; }
之前的题都是有手就行,下面的题需要一些能力了。
J是前缀和例题。令sum[i]=Σa[1],a[i],那么p和q覆盖的子序列和为sum[q]-sum[p-1],预处理前缀和数组只需sum[i]=sum[i-1]+a[i],由于q可能大于n,于是把sum[n+1]到sum[10000]都赋值为sum[n]。
#include<bits/stdc++.h> using namespace std; int a[1010],sum[10010]; int n,t,l,r; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i],sum[i]=sum[i-1]+a[i]; for(int i=n+1;i<=10000;i++) sum[i]=sum[i-1]; for(cin>>t;t;t--) { cin>>l>>r; cout<<sum[r]-sum[l-1]<<endl; } return 0; }
K是数论题。能同时被n和m整除的最小值是lcm(n,m),所有能同时被n和m整除的值是lcm(n,m)的整数倍。如果k>=lcm,把k减去k%lcm就是小于等于k的最大lcm的倍数,即为所求;如果k<lcm,那么不存在答案。
#include<bits/stdc++.h> using namespace std; long long gcd(long long a,long long b) { return b==0?a:gcd(b,a%b); } int main() { long long n,m,k; cin>>m>>n>>k; long long lcm=m/gcd(m,n)*n; if(k<lcm) cout<<-1; else cout<<k-k%lcm; return 0; }
L是一道高中数学题。问单词学错的种类,考虑求出所有的字母排列的方案数后-1即可。所有字母的排列方案是单词长度的阶乘/每个字母出现的次数的阶乘,很好写。
#include<bits/stdc++.h> using namespace std; int sum[300]; string s; int fac(int x) { int ans=1; while(x) ans=ans*x,x--; return ans; } int main() { cin>>s; for(int i=0;i<s.size();i++) sum[s[i]]++; int ans=fac(s.size()); for(int i='A';i<='Z';i++) ans=ans/fac(sum[i]); cout<<ans-1; return 0; }
M题要求数组元素的总和的方案数。注意到数组元素的总和的最小值是minn*(n-1)+maxx,最大值是maxx*(n-1)+minn,那么答案就是最大值减最小值+1=maxx*n-minn*n-2*(maxx-minn)+1。
#include<bits/stdc++.h> using namespace std; long long n,minn,maxx; int main() { scanf("%lld%lld%lld",&n,&minn,&maxx); cout<<maxx*n-minn*n-2*(maxx-minn)+1; return 0; }
N问闭区间1到n有多少个因子个数为3的数。首先1不是,大于1的数x至少有两个因子1和x它自己。要想满足因子个数是3,需要再来一个因子刚好是根号x。于是问题转化成了求1到根号n中的质数的个数,这些质数的平方都是有趣的数。求质数需要根号n,那么复杂度是根号n乘根号根号n=10^9,差不多能过。
#include<bits/stdc++.h> using namespace std; bool ask(long long x) { long long t=sqrt(x*1.0); for(int i=2;i<=t;i++) if(x%i==0) return 0; return 1; } int main() { int sum=0; long long n; cin>>n; for(long long i=2;i*i<=n;i++) if(ask(i)) sum++; cout<<sum; return 0; }
O题经典找不同。一般的题是给一个天平,用二分的方法。但是这道题直接给了一个能称任意重量的电子秤,直接第i堆拿出i个称重,本应是(1+n)*n/2g重,用实际的质量减它可以得到是那一堆硬币重量为2g,所以只需要分两种情况:1和大于1,答案分别是0和1。
#include<bits/stdc++.h> using namespace std; int main() { long long n;cin>>n; cout<<(n==1?0:1); return 0; }
P给的N首先要能表示成2的正整数次幂的和,表明奇数必然不行了。考虑用lowbit得到N二进制里的1的数量sum,然后不能直接拿着sum==3判断答案,比如8,sum=1但是可以表示成4+2+2。这提醒我们大于等于8的数们即使sum=1也可以,这时N为2^k,只需要拆分成N/2+N/4+N/4。大于6的数sum=2也可以,这时N=2^m+2^n,m>n,只需要拆分成2^(m-1)+2^(m-1)+2^n即可。sum=3当然可以了,sum>3的不行,不存在sum=0的正整数。
#include<bits/stdc++.h> using namespace std; int lowbit(int n) { return n&(-n); } int main() { int n,sum=0,t; cin>>n; if(n%2==1) { cout<<"NO"; return 0; } t=n; while(n) { n=n-lowbit(n); sum++; } if(t>=8&&sum==1||t>=6&&sum==2||sum==3) cout<<"YES"; else cout<<"NO"; return 0; }
Q题是个结论题:1/n是有限小数当且仅当n的质因子只有2和5。这是因为1和任意有限小数除以2,5都是有限小数,除以其他质数都是无限小数。于是把输入进来的n一直除以2和5,看看是不是剩下个1即可。
#include<bits/stdc++.h> using namespace std; int main() { int T,n; for(cin>>T;T;T--) { cin>>n; while(n%5==0) n=n/5; while(n%2==0) n=n/2; if(n==1) cout<<"YES"; else cout<<"NO"; cout<<endl; } return 0; }
R题博弈论我老不会了。用f[n][k]记录第k轮剩n个石子的后手输赢,为1时后手胜,为-1时后手输。那么
f[n][k]=1 <=>对任意i存在j使得f[n-i-j][k+1]=1 f[n][k]=-1<=>存在i对任意j使得f[n-i-j][k+1]=-1
于是可以写个记忆化搜索找规律:
int dfs(int n,int k) { //cout<<n<<' '<<k<<endl; if(f[n][k]) return f[n][k]; if(n==0) { f[n][k]=1; return 1; } if(k>=n) { f[n][k]=-1; return -1; } int flag; for(int i=1;i<=k;i++) { flag=1; for(int j=1;j<=k;j++) { if(n-i-j<0) break; if(dfs(n-i-j,k+1)==1) { flag=-1; break; } } if(flag==1) { f[n][k]=-1; return -1; } } f[n][k]=1; return 1; }
通过仔细观察大胆猜测可以得出,n=k*(k+1)/2时先手胜,否则先手输。于是我写了一个很笨的判断,复杂度TlogT+根号N。
#include<bits/stdc++.h> using namespace std; struct node { int x,i,ans; }o[1010]; bool m1(node a,node b) { return a.x<b.x; } bool m2(node a,node b) { return a.i<b.i; } int main() { int T;cin>>T; for(int i=1;i<=T;i++) cin>>o[i].x,o[i].i=i; sort(o+1,o+1+T,m1); for(int i=1,j=1;i*(i+1)/2-1<=o[T].x;i++) { while(j<=T&&o[j].x<=i*(i+1)/2-1) { if(o[j].x==i*(i+1)/2-1) o[j].ans=1; j++; } } sort(o+1,o+1+T,m2); for(int i=1;i<=T;i++) if(o[i].ans==1) cout<<"YES"<<endl; else cout<<"NO"<<endl; return 0; }
T好像是经典几何题但是我没学过,于是想到了用随机化来写,但是也一直不过(估计是我rand取数的时候写挂了).最后想到可以枚举这个面内的所有等距离的一系列点并判断是否同时在两个三角形内.判断点是否在三角形内我用的是计算该点和三个顶点中的两个围成的三个三角形的面积和是否等于三角形的面积.通过dev优秀的可以看时间的功能,我找到了0.03这个精度够小而且不T的点.
#include<bits/stdc++.h> using namespace std; long long sum=0,t; double x[7],y[7]; double d(int a,int b) { return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b])); } double s(int a,int b,int c) { double d1=d(c,a),d2=d(c,b),d3=d(a,b); double p=(d1+d2+d3)/2; return sqrt(p*(p-d1)*(p-d2)*(p-d3)); } int main() { for( int i=1;i<=6;++i) scanf("%lf%lf",&x[i],&y[i]); for(x[0]=0;x[0]<=5;x[0]+=0.003) { for(y[0]=0;y[0]<=5;y[0]+=0.003) { if(fabs(s(0,1,2)+s(0,2,3)+s(0,1,3)-s(1,2,3))<1e-6&&fabs(s(0,4,5)+s(0,5,6)+s(0,4,6)-s(4,5,6))<1e-6) ++sum; t++; } } double ans=sum*1.0/t*25; cout<<int(ans)<<'.'<<int(ans*10)%10; return 0; }