2018北大计算机学科夏令营机试题目#
题目链接
密码依然是fighting!
百练上北大夏令营的机试题目,全部拉到Virtual judge了 ,给我的感觉是2019的夏令营题目比2018难了一大截。不知道是偶然还是趋势。
A - 计算两个日期之间的天数
题目大意:计算两个日期差
题解:模拟水题,我用的是日期相减。没有写函数,导致中间一个变量用错了,Wa 2,绝了。以后还是多封装函数,不要写复制重复代码,就不贴代码了。
B - 回文子串
题目大意:
暴力找回文串
题解:
O(n^3)暴力即可
#include <iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<stdio.h>
#include<cmath>
using namespace std;
string str;
bool is_ok(int i,int j){
while(str[i]==str[j]){
i++;
j--;
if(j<=i)return 1;
}
return 0;
}
int main()
{
cin>>str;
int n=str.length();
for(int i=2;i<=n;i++){
for(int j=0;j<n;j++){
int ed=i+j-1;
if(ed<n){
if(is_ok(j,ed)){
for(int k=j;k<=ed;k++){
cout<<str[k];
}
cout<<endl;
}
}
}
}
return 0;
}
C - The Die Is Cast
题目大意:
双层联通块
题解:
不知道网上题解怎么写的。我是找到第一层联通快,然后找第二层联通快。写了两个DFS,代码冗长。
#include <iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<stdio.h>
#include<vector>
#include<cmath>
using namespace std;
char maze[55][55];
char temp[55][55];
char a[55][55];
bool vis[55][55];
bool vis2[55][55];
bool cur[55][55];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
int w,h;
bool in(int x,int y){
if(x>=0&&x<h&&y>=0&&y<w)return 1;
return 0;
}
void dfs2(int i,int j){
vis2[i][j]=1;
for(int ii=0;ii<4;ii++){
int x=i+dx[ii];
int y=j+dy[ii];
if(in(x,y)&&!vis2[x][y]&&temp[x][y]=='X'){
dfs2(x,y);
}
}
}
vector<int>ve;
void gao(){
memset(vis2,0,sizeof vis2);
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
temp[i][j]=maze[i][j];
if(temp[i][j]=='X'&&cur[i][j]==0)
temp[i][j]='.';
}
}
int cnt=0;
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
if(temp[i][j]=='X'&&!vis2[i][j]){
dfs2(i,j);
cnt++;
}
}
}
ve.push_back(cnt);
}
void dfs(int i,int j){
vis[i][j]=1;
cur[i][j]=1;
for(int ii=0;ii<4;ii++){
int x=i+dx[ii];
int y=j+dy[ii];
if(in(x,y)&&!vis[x][y]&&(maze[x][y]=='X'||maze[x][y]=='*')){
dfs(x,y);
}
}
}
int main()
{
int T=0;
while(cin>>w>>h&&w!=0){
cout<<"Throw "<<++T<<endl;
ve.clear();
memset(vis,0,sizeof(vis));
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
cin>>maze[i][j];
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
if(maze[i][j]=='*'||maze[i][j]=='X'){
if(!vis[i][j]){
memset(cur,0,sizeof cur);
dfs(i,j);
gao();
}
}
}
}
sort(ve.begin(),ve.end());
for(int i=0;i<ve.size();i++){
cout<<ve[i]<<" ";
}
cout<<endl<<endl;
}
return 0;
}
D - 食物链##
题目大意:
白书原题,很多人应该都写过
题解:
个人感觉采用一般并查集不是很好理解,下面采用带权并查集。带圈并查集才写了一个题,理解的肯定是不深刻。带权含义是每个节点到根节点的连线上存在一个权值,权值表示一定含义,节点合并时,需要加入权值信息,在路径压缩中,要保持每个节点到根节点权值不变,故需要做一些修改工作。本例中的三个种群分别代码节点到根节点的权值为0,1,2。
这个题神奇之处是,必须要scanf,cin会TLE。
#include <iostream>
#include<cstring>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=5e4+10;
int n,k;
int pre[N],val[N];
void init(){
for(int i=0;i<=n;i++)pre[i]=i,val[i]=0;
}
int find(int x){
int px=pre[x];
if(x!=pre[x]){
pre[x]=find(pre[x]);
val[x]=(val[x]+val[px])%3;
}
return pre[x];
}
int main()
{
scanf("%d%d",&n,&k);
int cnt=0;
init();
while(k--){
int d,x,y;
scanf("%d%d%d",&d,&x,&y);
int fx=find(x);
int fy=find(y);
if(x>n||y>n||(x==y&&d==2))cnt++;
else if(fx==fy&&(val[x]-val[y]+3)%3!=(d-1))cnt++;
else if(fx!=fy){
pre[fx]=fy;
val[fx]=(val[y]-val[x]+3+(d-1))%3;
}
}
cout<<cnt<<endl;
return 0;
}
E - 反正切函数的应用##
题目大意:
二元(b,c)一次解方程,求满足条件的最小b+c的值。
题解:
有点像高中数学题,但是我没有想到解法。参考网上的,就是变量代换,代换两轮,变成对勾函数的形式,在转折点取到最小值,由于要求整数,所以要枚举。不知道什么只需要枚举左边就好了。
#include <iostream>
#include<stdio.h>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int main()
{
long long a;
cin>>a;
for(int i=a;i>0;i--){
if((a*a+1)%i==0){
cout<<(i+(a*a+1)/i+2*a)<<endl;
break;
}
}
return 0;
}
F - Euro Efficiency##
题目大意:
给定六种硬币面值[1,100),求拼出[1,100]的每个数,最少需要参与的硬币个数为多少。(各种硬币个数不限,可以找钱(也就是做减法))。
题解:
完全背包,背包问题必须要很熟悉,经常出现。我这里做法有点不一样,只做了一次完全背包,之后枚举给钱的总钱数,取一个min。
这个N值要稍大,给的钱数理论最大值为100*100。
输出用%.lf wa了,也是很神奇,网上说和%.f没有区别,以后还是%.f为妙。
#include <iostream>
#include<stdio.h>
using namespace std;
int price[6];
const int N=20000+10;
int dp[6][N];
int main()
{
int T;
cin>>T;
while(T--){
for(int i=0;i<6;i++){
cin>>price[i];
}
for(int i=0;i<6;i++){
for(int j=1;j<N;j++){
if(i==0)dp[i][j]=j;
else{
for(int k=0;k<=i;k++){
if(j<price[k])break;
if(k==0)
dp[i][j]=dp[k][j-price[k]]+1;
else dp[i][j]=min(dp[i][j],dp[k][j-price[k]]+1);
}
}
}
}
int all=0;
int maxx=0;
int cur;
for(int i=1;i<=100;i++){
cur=1000;
for(int j=N-1;j>=i;j--){
cur=min(cur,dp[5][j]+dp[5][j-i]);
}
maxx=max(cur,maxx);
all+=cur;
}
printf("%.2f %d
",all/100.0,maxx);
}
return 0;
}
G - Tram##
题目大意:
简单最短路
题解:
建图十分简单,边上的权值0或者1或者INF,再使用任意最短路算法,Floyd最简单,注意Floyd首先枚举中间点。
#include <iostream>
#include<stdio.h>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=105;
const int INF=0x3f3f3f;
int n,a,b;
int dis[N][N];
int main()
{
cin>>n>>a>>b;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dis[i][j]=INF;
// if(i==j)dis[i][j]=0;
}
}
for(int i=1;i<=n;i++){
int k,e;
cin>>k;
for(int j=0;j<k;j++){
cin>>e;
if(j==0)dis[i][e]=0;
else dis[i][e]=1;
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++){
dis[j][k]=min(dis[j][k],dis[j][i]+dis[i][k]);
}
if(dis[a][b]>=INF)cout<<-1<<endl;
else cout<<dis[a][b]<<endl;
return 0;
}
H就不做了,逃。。。。。