2019BNUZ程序設計新生現場賽題解


題目來源

2019BNUZ程序設計新生現場賽


A.nsy上初中

Solution
  給定一個n,按照規則進行兩種操作:如果n爲奇數,n = 3n + 1;如果n爲偶數,n = n / 2。判斷哪些數進行無數次操作後,可以使得n變成1。
  規律題,只要不是0都可以進行無數次操作後變成1。優雅的暴力也能夠過。

AC_Code

#include<stdio.h>
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<stdio.h>
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<stdio.h>
#include<queue>
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<stdio.h>
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<stdio.h>
#include<string.h>
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<stdio.h>
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<stdio.h>
#include<string.h>
#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<stdio.h>
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<stdio.h>
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<stdio.h>
#include<math.h>
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<stdio.h>
#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<stdio.h>

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<stdio.h>
#include<math.h>
#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<stdio.h>
#include<string.h>
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 !!!
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章