比赛地址:http://qscoj.cn/contest/22/
第5-1关 吃饼(小数据)
分析:数据的范围只有1-3,所以可以将每种情况手工计算出来。1刀最多可切2块,2刀可切4块,3刀可切7块,4刀可切11块。注意,本题的坑点在于在场的女性除了小卿卿的F位朋友,还包括小卿卿自己,所以吉祥饼上得切F+1刀。
标程:
#include<bits/stdc++.h> using namespace std; int piece(int k) { if (k==1) return 2; if (k==2) return 4; if (k==3) return 7; if (k==4) return 11; } int main() { int m,f; cin>>m>>f; cout<<piece(m)+piece(f+1)<<endl; return 0; }
第5-2关 吃饼(中数据)
分析:设切N刀的最多饼块数为S(N),由第5-1关得S(1)=2;S(2)=4;S(3)=7;S(4)=11可以推出S(N)-S(N-1)=N,由此可以计算出S(N)的通项公式为S(N)=(N^2+N+2)/2,N虽然为10^9大小,但设变量时还是建议设成long long,不然相乘时会爆int.
标程:
#include<bits/stdc++.h> using namespace std; long long S(long long k) { return (k*k+k+2)/2; } int main() { long long m,f; cin>>m>>f; cout<<S(m)+S(f+1)<<endl; return 0; }
第5-3关 吃饼(大数据)
分析:由第5-2关可知,S(N)=(N^2+N+2)/2,这题N的大小为10^2333,故使用高精度的加法、乘法、除法便可解决此题。此题运算操作较多,需注意下进位的问题。或用JAVA中的大整数也可以解决这个问题。
C++标程:
#include<bits/stdc++.h> using namespace std; int m[2500],f[2500]; int m1[2500],f1[2500]; int c1[5000],c2[5000],c3[5000]; int main() { int i,j,lena,lenb; string a,b; bool flag; cin>>a>>b; lena=a.length();lenb=b.length(); for(i=0;i<lena;i++) m[lena-1-i]=a[i]-48; for(i=0;i<lenb;i++) f[lenb-1-i]=b[i]-48; f[0]++;j=0; while (f[j]==10) {f[j]=0;j++;f[j]++;} for(i=0;i<2400;i++) { m1[i]=m[i];f1[i]=f[i]; } m1[0]++;j=0; while (m1[j]==10) {m1[j]=0;j++;m1[j]++;} f1[0]++;j=0; while (f1[j]==10) {f1[j]=0;j++;f1[j]++;} for(i=0;i<2400;i++) for(j=0;j<2400;j++) { c1[i+j]+=m[i]*m1[j]; c2[i+j]+=f[i]*f1[j]; } for(i=0;i<4800;i++) c3[i]=c1[i]+c2[i]; for(i=0;i<4800;i++) { j=c3[i]/10;c3[i]%=10;c3[i+1]+=j; } for(i=0;i<4800;i++) { if (c3[i]%2==0) c3[i]/=2; else {c3[i-1]+=5;c3[i]/=2;} } c3[0]+=2;j=0;flag=false; while(c3[j]>=10) {c3[j]-=10;j++;c3[j]++;} for(i=4800;i>=0;i--) { if (i==0 || c3[i]) flag=true; if (flag) printf("%d",c3[i]); } cout<<endl; return 0; }
JAVA标程:
import java.util.*; import java.math.*; public class Main{ public static void main(String args[]){ Scanner cin=new Scanner(System.in); BigInteger m,f,m1,f1,ans1,ans2,ans; m=cin.nextBigInteger();f=cin.nextBigInteger(); f=f.add(BigInteger.valueOf(1)); m1=m.add(BigInteger.valueOf(1)); f1=f.add(BigInteger.valueOf(1)); ans1=m.multiply(m1); ans1=ans1.add(BigInteger.valueOf(2)); ans1=ans1.divide(BigInteger.valueOf(2)); ans2=f.multiply(f1); ans2=ans2.add(BigInteger.valueOf(2)); ans2=ans2.divide(BigInteger.valueOf(2)); ans=ans1.add(ans2); System.out.println(ans); } }
第6-1关 末位数(小数据)
分析:求M^N的个位数,M和N只有10^4大小,故直接for循环一遍,每次相乘后%10就是最终的答案。
标程:
#include<bits/stdc++.h> using namespace std; int main() { int m,n,k,i; cin>>m>>n>>k; for(i=1;i<=n;i++) { k*=m;k%=10; } cout<<k<<endl; }
第6-2关 末位数(中数据)
分析:求M^N的个位数,M和N有10^13大小,直接for肯定超时,所以我们可以通过分析下M的个位数,对于每一个M它的前几项的个位数找出他的规律。不难发现,个数为0、1、5、6的数,无论多少次方,其个位数还是其本身;个位数为2、3、7、8的数的乘方的个位数以4为周期在变化;个位数为4、9的数的乘方的个位数以2为周期在变化。分析出每一种情况,再特判一下任何一个数的0次方均为1,就可以AC了。
标程:
#include<bits/stdc++.h> using namespace std; int main() { long long m,n,k; cin>>m>>n>>k; if (n==0) {cout<<1<<endl;return 0;} m%=10;n%=4; if (m==0) k=0; if (m==1) k=1; if (m==2) { if (n==0) k=6; if (n==1) k=2; if (n==2) k=4; if (n==3) k=8; } if (m==3) { if (n==0) k=1; if (n==1) k=3; if (n==2) k=9; if (n==3) k=7; } if (m==4) { if (n==0) k=6; if (n==1) k=4; if (n==2) k=6; if (n==3) k=4; } if (m==5) k=5; if (m==6) k=6; if (m==7) { if (n==0) k=1; if (n==1) k=7; if (n==2) k=9; if (n==3) k=3; } if (m==8) { if (n==0) k=6; if (n==1) k=8; if (n==2) k=4; if (n==3) k=2; } if (m==9) { if (n==0) k=1; if (n==1) k=9; if (n==2) k=1; if (n==3) k=9; } cout<<k<<endl; return 0; }
第6-3关 末位数(大数据)
分析:这个题要求M^N的后k位数,其实就是M^N%P的问题,用到的知识点是快速幂。其中,P=10^k,注意输出时通过一些判断来保证有k位。
#include<bits/stdc++.h> using namespace std; long long modexp(long long a,long long b,int mod) { long long res=1; while (b) { a=a%mod; if (b & 1) res=res*a%mod; b=b>>1; a=a*a%mod; } return res; } int main() { long long m,n,k,ans; cin>>m>>n>>k; if (k==1) { ans=modexp(m,n,10); printf("%lld",ans); } if (k==2) { ans=modexp(m,n,100); if (ans<10) printf("0"); printf("%lld ",ans); } if (k==3) { ans=modexp(m,n,1000); if (ans<100) printf("0"); if (ans<10) printf("0"); printf("%lld ",ans); } if (k==4) { ans=modexp(m,n,10000); if (ans<1000) printf("0"); if (ans<100) printf("0"); if (ans<10) printf("0"); printf("%lld ",ans); } if (k==5) { ans=modexp(m,n,100000); if (ans<10000) printf("0"); if (ans<1000) printf("0"); if (ans<100) printf("0"); if (ans<10) printf("0"); printf("%lld ",ans); } return 0; }
第7-1关 排队(小数据)
分析:数据范围只有3,手工计算所有情况,发现只有“3 1 2”这种情况不行,便特判下这种情况输出“Fail”,其他均输出“Success”.
标程:
#include<bits/stdc++.h> using namespace std; int main() { int a[4],i,n; cin>>n; for(i=1;i<=n;i++) cin>>a[i]; if (n==3 && a[1]==3 && a[2]==1 && a[3]==2) cout<<"Fail"<<endl; else cout<<"Success"<<endl; return 0; }
第7-2关 排队(中数据)
分析:数据范围有10,可以通过DFS方式把所有的入栈出栈的可能计算出来,再判断是否与汪老师要求的序列进行匹配。
标程:
#include<bits/stdc++.h> using namespace std; int s[25],a[13],b[13],n; int t[2]; bool flag; stack<int> st; void dfs(int k) { int i,j,cnt; if (k==2*n+1) { if (t[0]!=t[1]) return ; cnt=0;j=1;while(!st.empty()) st.pop(); for(i=1;i<=2*n;i++) { if (s[i]==0) st.push(j++); else {cnt++;b[cnt]=st.top();st.pop();} } bool g; g=true; for(i=1;i<=n;i++) if (a[i]!=b[i]) g=false; if (g) flag=true; return ; } for(i=0;i<2;i++) { s[k]=i;t[i]++; if (t[1]<=t[0]) dfs(k+1); t[i]--; } } int main() { int i; cin>>n; for(i=1;i<=n;i++) cin>>a[i]; flag=false;dfs(1); if (flag) cout<<"Success"<<endl; else cout<<"Fail"<<endl; return 0; }
第7-3关 排队(大数据)
分析:其实这道题就是一道模拟题。用一个光标记录新队列当前元素的位置,一开始这个位置为1,先将原队列的头元素压入栈中,再不断判断栈顶元素是否为新队列指向的元素,如果是的话就弹出栈顶元素,并且将新队列位置右移;不是的话就再从原队列中压入元素。最后检查栈是否为空或者光标是否移到最右边的位置,来判断是否排队成功。
标程:
#include<bits/stdc++.h> using namespace std; stack<int> q; int main() { int n,i,j; int a[60010]; cin>>n; for(i=1;i<=n;i++) scanf("%d",&a[i]); j=1; for(i=1;i<=n;i++) { q.push(i); while(!q.empty() && q.top()==a[j]) {q.pop();j++;} } if (q.empty()) cout<<"Success"<<endl; else cout<<"Fail"<<endl; return 0; }
第8-1关 默契值(小数据)
分析:如果只有2名队员,答案就是他们的默契值;如果有4名队员,刘老师必须得跟其中与自己默契值最大的队员组队,在合理的可能中,再判断剩下两名队员哪组默契值最小,两种默契值相加就是答案。
标程:
#include<bits/stdc++.h> using namespace std; int main() { int a,b,c,d,e,f,n,maxx,minx; cin>>n; if (n==2) { cin>>a; cout<<a<<endl; return 0; } cin>>a>>b>>c>>d>>e>>f; maxx=max(a,max(b,c));minx=0x3f3f3f3f; if (a==maxx) minx=min(minx,f); if (b==maxx) minx=min(minx,e); if (c==maxx) minx=min(minx,d); cout<<maxx+minx<<endl; return 0; }
第8-2关 默契值(中数据)
分析:队员最大有10名,所以可以用全排列的方法做这道题。奇偶相邻的两名队员便为配对的队员,得保证刘老师得跟与自己默契值最大的队员配对,再从剩下的队员找配对的最小默契值和。还是注意特判下N=2的情况。
标程:
#include<bits/stdc++.h> using namespace std; int main() { int n,i,j,k,maxx,minx,res; int a[25][25],b[25]; cin>>n; for(i=1;i<=n-1;i++) for(j=1;j<=n-i;j++) cin>>a[i][i+j]; if (n==2) { cout<<a[1][2]<<endl; return 0; } maxx=-1;minx=0x3f3f3f3f; for(i=2;i<=n;i++) maxx=max(a[1][i],maxx); for(i=1;i<=n;i++) b[i]=i; do { if (a[1][b[2]]<maxx) continue;res=0; for(i=2;i<=n/2;i++) { j=b[2*i-1];k=b[2*i]; res+=a[min(j,k)][max(j,k)]; } minx=min(res,minx); }while(next_permutation(b+2,b+1+n)); cout<<maxx+minx<<endl; return 0; }
第8-3关 默契值(大数据)
分析:N的大小为20,用全排列或者DFS的方式肯定会超时。所以可以采用DP+记忆化搜索的方式来完成此题。需要开一个1<<20的数组,将每个数转化为二进制,如果某一位的值为0,则表明该种情况不包括其位置所对应的队员;如果某一位的值为1,则表明该种情况包括其位置所对应的队员。初始化时将d[x]所有值赋为1个非常大的数,将d[0]赋为0.在dp函数中每次先找出当前没配对的第1名队员,再从剩下没配对的队员中依次寻找与它配对,将s一开始赋值为(1<<n)-1;s=s^(1<<i)^(1<<j)就表示去掉第i名队员和第j名队员剩下的情况。转移方程便为d[s]=min(d[s],dp(s^(1<<i)^(1<<j))+a[i][j]);总之,只要将这道题的方法搞清楚了,解决它也不是什么难事。
标程:
#include<bits/stdc++.h> using namespace std; int a[20][20],d[1<<20]; int n; int inf=0x3f3f3f3f; int dp(int s) { int i,j; if (d[s]!=inf) return d[s]; for(i=0;i<n;i++) if (s & (1<<i)) break; for(j=i+1;j<n;j++) if (s & (1<<j)) d[s]=min(d[s],dp(s^(1<<j)^(1<<i))+a[i][j]); return d[s]; } int main() { int t,s,maxx,minx,i,j; cin>>n; for(i=0;i<n-1;i++) for(j=1;j<n-i;j++) cin>>a[i][i+j]; if (n==2) { cout<<a[0][1]<<endl; return 0; } maxx=-1;minx=inf; for(i=1;i<n;i++) if (a[0][i]>maxx) maxx=a[0][i]; for(i=1;i<n;i++) { if (a[0][i]==maxx) { for(j=0;j<(1<<n);j++) d[j]=inf; s=(1<<n)-1; s=s^(1<<0)^(1<<i); d[0]=0; minx=min(minx,dp(s)); } } cout<<maxx+minx<<endl; return 0; }