2019BNUZ程序设计新生现场赛题解

目录

  • 题目来源
    • A.nsy上初中
    • B.nsy上高中
    • C.我要看憨色直播
    • D.单身狗也想要凑对
    • E.暗恋的密码
    • F.舔狗的心酸,不用你来拆穿
    • G.吃不到的雪糕
    • H.我有病,你有药吗?
    • I.题到签
    • J.校队选拔
    • K.我们是冠军
    • L.The World砸瓦鲁多
  • 写在最后


题目来源

2019BNUZ程序设计新生现场赛


A.nsy上初中

Solution
  给定一个n,按照规则进行两种操作:如果n为奇数,n = 3n + 1;如果n为偶数,n = n / 2。判断哪些数进行无数次操作后,可以使得n变成1。
  规律题,只要不是0都可以进行无数次操作后变成1。优雅的暴力也能够过。

AC_Code

#include
int main() {
	int t, cas = 0, n;
	scanf("%d",&t);
	while(t--) {
		scanf("%d",&n);
		printf("Case #%d:\n",++cas);
		if(n == 0) printf("nsynb!\n");
		else printf("yjznb!\n");
	}
	return 0;
}

B.nsy上高中

Solution
  数学排列组合中的挡板问题,把n本书分成m份。可以看作在(n-1)个缝隙中,插入(m-1)块挡板的问题。
实际上是求解
在这里插入图片描述
需要注意的是
在这里插入图片描述
所以在大多数情况下,答案不一定会超过1012

AC_Code

#include
const long long mod = 1e12;
long long sum;
int main()
{
    int n,k;
    int T,cas=1;
    scanf("%d",&T);
    while(T--)
    {
    	scanf("%d %d",&n,&k);
    	n -= 1, k -= 1;
        int c;
		if(n-k<k) c = n-k;
		else 
		c = k; 
        sum = 1;
        for(int i=1;i<=c;i++)
        {
            sum = sum*(n-i+1)/i;
            if(sum > mod) break;
        }
        if(sum > mod)
            printf("Case #%d:\n%lld\n",cas++,mod);
        else
            printf("Case #%d:\n%lld\n",cas++,sum);
    }
    return 0;
}

C.我要看憨色直播

Solution
  走迷宫问题,从起点坐标走到终点坐标。BFS模板题,新生培训课程给出的BFS模板经过修改即可得到答案。

AC_Code

#include
#include
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 105;
int n,m,ans,end_x,end_y;
int vis[maxn][maxn];
char maze[maxn][maxn];

int dir[4][2] = { {-1,0}, {1, 0}, {0,-1}, {0, 1} };

struct node {
	int x,y,step;
};
queue<node> q;

void bfs() {
	while(q.size()) {
		node now_ = q.front();
		node next_;
		q.pop();
		if(now_.x == end_x && now_.y == end_y) {
			ans = now_.step;
			return;
		}
		for(int i=0; i<4; i++) {
			next_.x = now_.x + dir[i][0];
			next_.y = now_.y + dir[i][1];
			next_.step = now_.step + 1;
			if(next_.x <= 0 || next_.x > n || next_.y <= 0 || next_.y > m) continue;
			if(vis[next_.x][next_.y] == 1 || maze[next_.x][next_.y] == '0') continue;
			vis[next_.x][next_.y] = 1;
			q.push(next_);
		}
	}
}

int main() {
	int t, cas = 0;
	scanf("%d",&t);
	while(scanf("%d%d",&n,&m) != EOF) {
		for(int i=1; i<=n; i++) {
			for(int j=1; j<=m; j++) {
				vis[i][j] = 0;
			}
		}
		node start_;
		for(int i=1; i<=n; i++) {
			getchar();
			for(int j=1; j<=m; j++) {
				scanf("%c",&maze[i][j]);
			}
		}
		scanf("%d%d%d%d",&start_.x,&start_.y,&end_x,&end_y);
		start_.step = 0;
		vis[start_.x][start_.y] = 1;
		printf("Case #%d:\n",++cas);
		ans = inf;
		while(q.size()) q.pop();
		q.push(start_);
		if(maze[start_.x][start_.y] == '1' && maze[end_x][end_y] == '1') {
			bfs();
		}
		if(ans == inf) {
			puts("-1");
		}
		else {
			printf("%d\n",ans);
		}
	}
	return 0;
}

D.单身狗也想要凑对

Solution
  从a到b中找出一个x,从c到d中找出一个y,使得x*y是2019的倍数。
  数学题,把2019拆分成1*2019和3*673,然后在a~b和c~d中找出对应的倍数的个数,去掉重复部分即可得到。暴力无论多么优雅都是过不了的。

AC_Code

#include
int main() {
	int t, cas = 0;
	scanf("%d",&t);
	while(t--) {
		long long a,b,c,d;
		scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
		long long total = 0;
		long long cd2019 = d / 2019 - (c-1) / 2019;
		long long ab2019 = b / 2019 - (a-1) / 2019;
		total += cd2019 * (b-a+1) + ab2019 * (d-c+1) - cd2019 * ab2019;
		long long cd3 = d / 3 - (c-1) / 3 - cd2019;
		long long ab3 = b / 3 - (a-1) / 3 - ab2019;
		long long cd673 = d / 673 - (c-1) / 673 - cd2019;
		long long ab673 = b / 673 - (a-1) / 673 - ab2019;
		total += cd3 * ab673 + ab3 * cd673;
		printf("Case #%d:\n",++cas);
		printf("%lld\n",total);
	}
	return 0;
}

E.暗恋的密码

Solution
  字符串模拟题,把密码中的[ ]中的内容输出对应次数,[ ]外的内容照常输出。找到[ ]后先判断框内数字大小,数字可能有好几位。

AC_Code

#include
#include
int main() {
	int t, cas = 0;
	char str[30010];
	char temp[30010];
	scanf("%d",&t);
	while(t--) {
		scanf("%s",str);
		printf("Case #%d:\n",++cas);
		for(int i=0; i<strlen(str); i++) {
			if(str[i] == '[') {
				i++;
				int n = 0,index = 0;
				while(str[i] != ']') {
					if(str[i] <= '9' && str[i] >= '0') {
						n = n * 10 + str[i] - '0';
						i++;
						index = 0;
					} 
					else {
						temp[index++] = str[i];
						i++;
					}
				}
				for(int j=0; j<n; j++) {
					for(int k=0; k<index; k++)
						printf("%c",temp[k]);
				}
			} 
			else {
				printf("%c",str[i]);
			}
		}
		printf("\n");
	}
	return 0;
}

F.舔狗的心酸,不用你来拆穿

Solution
  有一张n点的完全无向图,点的标号是从1到n,其中边(i,j)的长度是i xor j,现在需要求出点1到点n的最短路的长度。
  规律题,我们可以发现直接从1到n是所有路径中最短的,其他路径都不会比直接从1到n更短。

AC_Code

#include
int main() {
    int T,cas=1;
    scanf("%d",&T);
    while(T--) {
        int n;
        scanf("%d",&n);
        printf("Case #%d:\n%d\n",cas++,1^n);
    }
    return 0;
}

G.吃不到的雪糕

Solution
  找字符串中,有多少个子序列包含先a个连续的C,再b个连续的S,最后c个连续的M。

解法一:动态规划
  开一个结构体数组记录一个字符串中每个相同字符连续区间的序号id(假设C为1,S为2,M为3)和他所能得到不同种类数目value。 比如SSSS,b=2,那么这一个区间的id = 2, value = max(0, 4-2+1)。
  然后开始遍历结构体数组,如果当前id是1,ans1 += value,如果当前id是2,那么ans2 += ans1 * value,如果id为3,sum = ans2 * value。

解法二:前缀和
  开两个数组记录前缀和后缀和,用于表示在当前位置,前方(pre)和后方(suf)有多少组符合当前需求的C和M,然后再进行遍历寻找符合条件的S,加上pre[i] * suf[i] 即可。

First AC_Code

#include
#include
#define maxn 300010
char s[maxn],s1[maxn],s2[maxn];
int len1[maxn],len2[maxn],len3[maxn];
int sum1[maxn],sum2[maxn],sum3[maxn];
#define mod 998244353

struct node
{
	long long value_;
	int id_;
}k[maxn];

int judge(char ch){
    if(ch == 'C') return 1;
    else if(ch == 'S') return 2;
    else return 3;
}

int maxx(int a, int b) {
	return a > b ? a : b;
}

int main()
{
	int t;
	int times = 0;
	scanf("%d",&t);
	while(t--){
		int n,a,b,c;
        int cnt1, cnt2, cnt3;
        cnt1 = cnt2 = cnt3 = 0;
		scanf("%d%d%d%d",&n,&a,&b,&c);
		scanf("%s",s);
		printf("Case #%d:\n",++times);
		int len = strlen(s);
		int num = 1;
		int cnt = 0;
		char ch = s[0];
		long long sum = 0;
		for(int i=1; i<len; ++i){
            if(s[i] != ch){
                if(judge(s[i-1]) == 1){
                    num = maxx(0, num-a+1);
                }
                else if(judge(s[i-1]) == 2){
                    num = maxx(0, num-b+1);
                }
                else num = maxx(0,num-c+1);
                k[cnt].value_ = num;
                k[cnt++].id_ = judge(s[i-1]);
                num = 1;
                ch = s[i];
            }
            else num += 1;
        }
        if(judge(s[len-1]) == 1){
            num = maxx(0, num-a+1);
        }
        else if(judge(s[len-1]) == 2){
            num = maxx(0, num-b+1);
        }
        else num = maxx(0,num-c+1);
        k[cnt].value_ = num;
        k[cnt++].id_ = judge(s[len-1]);
        int ans1 = 0, ans = 0;
        for(int i=0; i<cnt; ++i){
            if(k[i].id_ == 1) ans1 += k[i].value_;
            else if(k[i].id_ == 2) ans += ans1 * k[i].value_;
            else sum += ans * k[i].value_, sum %= mod;
        }
        printf("%d\n",sum);
	}
	return 0;
}

Second AC_Code

#include
int main() {
	int t, cas = 0;
	char str[10000+5];
	long long pre[10000+5], suf[10000+5], mod = 998244353;
	scanf("%d",&t);
	while(t--) {
		int n,a,b,c;
		scanf("%d%d%d%d",&n,&a,&b,&c);
		scanf("%s",str);
		int cot = 0, cnt = 0;
		long long total = 0;
		for(int i=0; i<n; i++) {
			if(str[i] == 'C') {
				if(i != 0 && str[i] != str[i-1]) {
					cot = 1;
				}
				else {
					cot++;
				}
				if(cot >= a) {
					cnt++;
				}
			}
			pre[i] = cnt;
		}
		cot = 0, cnt = 0;
		for(int i=n-1; i>=0; i--) {
			if(str[i] == 'M') {
				if(i != n-1 && str[i] != str[i+1]) {
					cot = 1;
				}
				else {
					cot++;
				}
				if(cot >= c) {
					cnt++;
				}
			}
			suf[i] = cnt;
		}
		cot = 0, cnt = 0;
		for(int i=0; i<n; i++) {
			if(str[i] == 'S') {
				if(i != 0 && str[i] != str[i-1]) {
					cot = 1;
				}
				else {
					cot++;
				}
				if(cot >= b) {
					total += (pre[i] * suf[i]) % mod;
					total %= mod;
				}
			}
		}
		printf("Case #%d:\n%lld\n",++cas,total); 
	}
	return 0;
}

H.我有病,你有药吗?

Solution
  用尽可能少个2的幂(指的是2的x次方)来表示n。

解法一:
  把n转换成2进制,数有多少个1。

解法二:
  不断枚举2的幂直到大于n为止,然后往回筛选。

First AC_Code

#include
int main() {
	int t, cas = 0;
	scanf("%d",&t);
	while(t--){
		long long n;
		scanf("%lld",&n);
		long long ans = 0;
		while(n){
			ans += n & 1;
			n>>=1;
		}
		printf("Case #%d:\n%lld\n",++cas,ans);
	}
	return 0;
}

Second AC_Code

#include
#include
int main() {
	int t, cas = 0;
	scanf("%d",&t);
	while(t--) {
		long long n;
		scanf("%lld",&n);
		int cnt = 0;
		long long sum = 0;
		while(sum < n) {
			sum += pow(2,cnt);
			cnt++;
		}
		int ans = cnt;
		while(sum > n) {
			if(sum - pow(2,cnt) >= n) {
				sum -= pow(2,cnt);
				ans--;
			}
			cnt--;
		}
		printf("Case #%d:\n%d\n",++cas,ans);
	}
	return 0;
}

I.题到签

Solution
  作业型签到题,手写min函数。

AC_Code

#include
#define eps 10e-6
double minn(double a, double b, double c) {
	double ans;
	if(a - b <= eps) ans = a;
	else ans = b;
	if(ans - c <= eps) return ans;
	else return c;
}
int main() {
	int t, cas = 0;
	scanf("%d",&t);
	while(t--) {
		double a,b,c;
		scanf("%lf%lf%lf",&a,&b,&c);
		printf("Case #%d:\n%.2lf\n",++cas,minn(a,b-c,c)-minn(a,b,b-c)+minn(a-b,b,c));
	}
	return 0;
}

J.校队选拔

Solution
  给出a名数论选手,b名图论选手,c名什么也不会的选手。每队必须3人,每队至少有一名数论选手和一个图论选手,问最多可以有几支队伍。
  数学题。首先我们需要知道,组成尽可能多的队伍两个限制:1.总人数是否能够凑够这么多队伍;2.数论和图论选手是否能够满足每支队伍分配一人或以上。
  因此我们只需要找出这两个限制条件中最小的一个数,即min( (a+b+c) / 3, a, b)。

AC_Code

#include

long long minn(long long a, long long b) {
	return a < b ? a : b;
}

int main() {
	int t, cas = 0;
	scanf("%d",&t);
	while(t--) {
		long long a,b,c;
		scanf("%lld%lld%lld",&a,&b,&c);
		long long ans = minn(a,b);
		ans = minn(ans,(a+b+c)/3);
		printf("Case #%d:\n%lld\n",++cas,ans);
	}
	return 0;
}

K.我们是冠军

Solution
  题目会给你卡莎的攻击力,攻速,敌人的血量和护甲,然后给出装备的数目,计算卡莎击败对手需要的时间。
  模拟题,因为唯一被动只会生效一次,所以可以先计算出固定穿甲和百分比穿甲,由于同时拥有百分比护甲穿透和固定穿甲,先计算百分比护甲穿透,再计算固定穿甲,所以对方的护甲值
S = S * (1 - 护甲穿透),S = S - 固定穿甲,然后根据装备的数量计算攻击力和攻击速度(非唯一被动,可叠加),最后用H(血量) / (ack * x)(攻击力*攻速)算出时间。

AC_Code

#include
#include
#define inf 0x3f3f3f3f
struct equipment{
	int attack;
	double rate;
	double speed;
	int pojia;
}equ[10],total;
int vis[10];
int h,a,s,n;
double x;
double ans = inf;
int main()
{
	equ[0].attack = 50;equ[0].rate = 0.35;
	equ[1].attack = 65;equ[1].pojia = 15;
	equ[2].attack = 60;equ[2].pojia = 15;
	equ[3].attack = 55;equ[3].pojia = 15;
	equ[4].speed = 0.45;
	int T,cas=1;
	scanf("%d",&T);
	while(T--)
	{
		ans = 0;
		for(int i=0;i<10;i++) vis[i] = 0;		
		scanf("%d %lf",&a,&x);
		scanf("%d %d",&h,&s);
		for(int i=0;i<5;i++)
			scanf("%d",&vis[i]); 
		total.attack = 0;
		total.rate = 0;
		total.speed = 0;
		total.pojia = 0;
		total.speed = x;
		for(int i=0;i<5;i++)
		{
			if(vis[i]==0) continue;
			total.attack += vis[i]*equ[i].attack;
			total.speed *= pow((1+equ[i].speed),vis[i]);
			if(total.speed>2.5) total.speed = 2.5; 
			total.pojia += equ[i].pojia;
			total.rate += equ[i].rate;
		}
		double hurt = (a+total.attack)*(1-(s*(1-total.rate)-total.pojia)/(100+s*(1-total.rate)-total.pojia));
		ans = h/(hurt*total.speed);
		printf("Case #%d:\n%.2f\n",cas++,ans);
	}
	return 0;
}

L.The World砸瓦鲁多

Solution
  把n个东西放进m个箱子,使得每一个箱子内容都尽可能的小。
  首先求出箱子的容量范围是多少。然后暴力增加求出就好了,模拟放入的过程就好了。

AC_Code

#include
#include
int arr[200010],vis[200010];
int main() {
	int t, cas = 0;
	scanf("%d",&t);
	while(t--) {
		int n,m;
		long long sum = 0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) {
			scanf("%d",&arr[i]);
			sum += arr[i];
		}
		for(int i=1;i<n;i++) {
			for(int j=1;j<n;j++) {
				if(arr[j] < arr[j+1]) {
					int temp = arr[j];
					arr[j] = arr[j+1];
					arr[j+1] = temp;
				}
			}
		}
		int ans = 0;
		int avg = arr[1];
		if(avg < sum / m) avg = sum / m;
		for(int i=avg; i<=sum; i++) {
			for(int j=1;j<=n;j++) vis[j] = 0;
			int cnt = 0;
			for(int j=1;j<=m;j++) {
				int res = i;
				for(int k=1;k<=n;k++) {
					if(!vis[k] && arr[k] <= res) {
						res -= arr[k];
						cnt++;
						vis[k] = 1;
					}
				}
			}
			if(cnt == n) {
				ans = i;
				break;
			}
		}
		printf("Case #%d:\n%d\n",++cas,ans);
	}
	return 0;
}

写在最后

  一年一度的新生赛终于结束了。
  去年作为参赛者参加比赛,第一次感受到了ACM竞赛的魅力。
  今年作为出题人,举办人,主持人,我也很想把最好的比赛体验带给大家,尽量的出简单题,模拟题;准备颜色鲜艳的气球;准备丰富的礼物……
  我真的从开学一直忙到现在……从欢乐赛、网络赛再到现场赛,出了好多题目也改了好多题目。着大家辛苦艰难的做题真的看到了当初的自己,别人口中的签到题,到了自己这里写起来就是个大模拟自己以为能写的题目听别人一讲原来和自己的思路完全不一样。
  ACM对我来说真的很有魅力,教会了我思维,也让我体会到了“算法”和“数据结构”的优雅。让我从一个敲代码的“莽夫”学会思考怎么样的解法才是“最优”。
  不知道这场比赛会不会让新生自闭但是真的希望大家不要怀疑自己的能力。
  加油!ACMer永不放弃!!!BNUZ-ACM Fighting !!!
2019BNUZ程序设计新生现场赛题解_第1张图片

你可能感兴趣的:(心得,补题库)