2018年北京大学软件工程学科夏令营上机考试#
题目链接
密码:fighting
总体题目难度不大,都是常规题。就看谁写的快了。
A - 第K小最简真分数##
这个题不是这次考试的题目,A应该是签到,由于没有找到原题,找到一个"题目类似"作为补充。但是这个应该比其他任何题目都难。
题意:###
给一个整数N,请你求出以N为分母的最简(既约)真分数中第K小的是多少 。
$1 <= N <= 10000000000 且 1 <= K <= φ(N) $
题解:###
- 唯一分解定律分解出的质数不超过20个。
- 1到x有因子y的树的个数为$frac xy $个。
- 容斥定理去重。
- 对于任何一个分母N,包含其唯一分解定理分解出来的质因子,就一定不是真分数,使用容斥定理计算包含至少一个质因子的个数。求第K大,一般想到二分,二分答案即可。
代码:###
#include <iostream>
#include<stdio.h>
#include<string>
#include<cstring>
using namespace std;
#define ll long long
ll a[2000],b[2000],tot;//a是因子乘积,b是代表奇偶。
ll K,N,l,r,Mid,ans;
ll prim[20],cnt;
void divide(ll n){
ll y=n;
for(ll i=2;i*i<=n;i++){
if(y%i==0)prim[cnt++]=i;
while(y%i==0&&y)y/=i;
}
if(y>1)prim[cnt++]=y;
}
void dfs(ll x,int y,int num){
if(y==cnt){//遍历到最后一个因子
if(x==1)return ;
a[tot]=x;b[tot++]=(num&1)?1:-1;
return;
}
dfs(x*prim[y],y+1,num+1);
dfs(x,y+1,num);
}
bool check(ll n){
ll num=0;//与n不互质的个数
for(ll i=0;i<tot;i++){
num+=n/a[i]*b[i];
}
return n-num>=K;
}
int main()
{
cnt=0;
tot=0;
scanf("%lld%lld",&N,&K);
divide(N);
dfs(1,0,0);
l=0;r=1e12;
while(l<=r){
Mid=(l+r)/2;
if(check(Mid)){
ans=Mid;
r=Mid-1;
}
else l=Mid+1;
//cout<<l<<" "<<r<<endl;
}
printf("%lld
",ans);
return 0;
}
B - n-gram串频统计##
题意:###
水题模拟
题解:###
暴力,使用map,可能写法比较麻烦
代码:###
#include <iostream>
#include <string>
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include<map>
using namespace std;
map<string,int>map_cnt;
map<string,int>map_rk;
int main()
{
int n;
cin>>n;
string str;
cin>>str;
for(int i=0;i<=str.size()-n;i++){
string temp="";
for(int j=0;j<n;j++){
temp+=str[i+j];
}
map_cnt[temp]++;
}
map<string,int>::iterator it;
int cnt=0;
for(it=map_cnt.begin();it!=map_cnt.end();it++){
cnt=max(cnt,(*it).second);
}
if(cnt<2){
cout<<"NO"<<endl;
return 0;
}
int ans=0;
for(it=map_cnt.begin();it!=map_cnt.end();it++){
if((*it).second==cnt)ans++;
}
cout<<cnt<<endl;
for(int i=0;i<=str.size()-n;i++){
string temp="";
for(int j=0;j<n;j++){
temp+=str[i+j];
}
if(map_cnt[temp]==cnt){
if(!map_rk.count(temp)){
cout<<temp<<endl;
}
map_rk[temp]++;
}
}
return 0;
}
C - 垂直直方图##
题意:###
水题模拟
题解:###
暴力,读入一行可以使用gets,fgets,getline等函数。
代码:###
#include <iostream>
#include <string>
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include<map>
using namespace std;
map<char,int>map_cnt;
int main()
{
int max_cnt=0;
for(int i=0;i<4;i++){
string str;
getline(cin,str);
for(int i=0;i<str.size();i++){
char ch=str[i];
if(ch>='A'&&ch<='Z'){
map_cnt[ch]++;
max_cnt=max(max_cnt,map_cnt[ch]);
}
}
}
for(int i=max_cnt;i>0;i--){
for(int j=0;j<26;j++){
if(map_cnt['A'+j]==i){
cout<<"* ";
map_cnt['A'+j]--;
}
else cout<<" ";
}
cout<<endl;
}
for(int i=0;i<26;i++){
cout<<char('A'+i)<<" ";
}
cout<<endl;
D - Word-Search Wonder##
题意:###
在字符矩阵中,找到某个字符串在矩阵中的起点和终点。(保证最多一个解)
题解:###
暴力,八个方向探索。
代码:###
#include <iostream>
#include <string>
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include<map>
using namespace std;
const int N=105;
char maze[N][N];
int n;
void output(int x1,int y1,int x2,int y2){
cout<<x1+1<<','<<y1+1<<" "<<x2+1<<","<<y2+1<<endl;
}
void gao(string str){
int len=str.size();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(maze[i][j]==str[0]){
int n_i=i,n_j=j;
//down
while(n_i<n&&maze[n_i][j]==str[n_i-i])n_i++;
if(n_i-i==len){
n_i--;
output(i,j,n_i,j);
return;
}
//up
n_i=i,n_j=j;
while(n_i>=0&&maze[n_i][j]==str[i-n_i])n_i--;
if(i-n_i==len){
n_i++;
output(i,j,n_i,j);
return;
}
//r
n_i=i,n_j=j;
while(n_j<n&&maze[i][n_j]==str[n_j-j])
n_j++;
if(n_j-j==len){
n_j--;
output(i,j,i,n_j);
return;
}
//l
n_i=i,n_j=j;
while(n_j>=0&&maze[i][n_j]==str[j-n_j])n_j--;
if(j-n_j==len){
n_j++;
output(i,j,i,n_j);
return;
}
//x
n_i=i,n_j=j;
while(n_j<n&&n_i<n&&maze[n_i][n_j]==str[n_j-j])n_j++,n_i++;
if(n_j-j==len){
n_j--;n_i--;
output(i,j,n_i,n_j);
return;
}
n_i=i,n_j=j;
while(n_j>=0&&n_i>=0&&maze[n_i][n_j]==str[j-n_j])n_j--,n_i--;
if(j-n_j==len){
n_i++;n_j++;
output(i,j,n_i,n_j);
return;
}
n_i=i,n_j=j;
while(n_j>=0&&n_i<n&&maze[n_i][n_j]==str[j-n_j])
n_j--,n_i++;
if(j-n_j==len){
n_j++;n_i--;
output(i,j,n_i,n_j);
return;
}
n_i=i,n_j=j;
while(n_j<n&&n_i>=0&&maze[n_i][n_j]==str[n_j-j])n_j++,n_i--;
if(n_j-j==len){
n_i++;n_j--;
output(i,j,n_i,n_j);
return;
}
}
}
}
puts("Not found");
}
E - A Mini Locomotive##
题意:###
一共(n)车厢,每个车厢人数为(a_i),可以带走三段连续的车厢人数,求最多可以带走的人数。
题解:###
贪心不可取,DP,类似01背包问题。
(dp[i][j])表示遍历到第(i)个人,总共使用了(j)次的最大答案数量。
代码:###
#include <iostream>
#include <string>
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include<map>
using namespace std;
const int N=5e4+10;
int a[N];
int pre[N];
int dp[N][4];
int n,len;
int main()
{
int T;
cin>>T;
while(T--){
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<3;j++)
dp[i][j]=0;
for(int i=0;i<n;i++){
cin>>a[i];
if(i==0)pre[i]=a[i];
else pre[i]=pre[i-1]+a[i];
}
cin>>len;
for(int i=0;i<n;i++){
for(int j=1;j<=3;j++){
if(i<len)
dp[i][j]=pre[i];
else
dp[i][j]=max(dp[i-1][j],dp[i-len][j-1]+pre[i]-pre[i-len]);
//cout<<dp[i][j]<<" ";
}
// cout<<endl;
}
cout<<dp[n-1][3]<<endl;
}
return 0;
}
EFG#
都不难,一个贪心;一个裸的最小生成树,这个题已经遇到了几次了;一个最小哈夫曼树的模拟。这三题都不写了。
I#
没找到题目来源。