NWAFU第四届新生程序设计大赛题解

第四届新生程序设计大赛题解
by-NWAFU ACM团队

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0550x4ns-1639192646155)(C:\Users\Xyr\AppData\Roaming\Typora\typora-user-images\image-20211211103913985.png)]

A 我想变成ACMer
题目解析

​ 简单签到题,注意转义字符的使用。

题解代码
#include <stdio.h>
#include <stdlib.h>
int main()
{
    printf("I want to be an ACMer\\n\\n");
    return 0;
}
B 出现次数最多的字母
题目解析

读入字符串,从到到尾遍历字符串,统计每个字符出现的次数。排序后将出现次数最多的几个输出即可。

题解代码
#include<stdio.h>
#include<string.h>
#define LL long long

int maxx=0,sum[30],ans[30],tot = 0;
char s[1010];

int MAX(int a,int b)
{
	if( a>b ) return a;
	else return b;
}

void solve()
{
	scanf("%s",s);
	int len = strlen(s);
	for(int i=0;i<len;i++)
	{
		sum[ s[i]-'a' ]++;
	}
	for(int i=0;i<=25;i++) maxx = MAX( maxx , sum[i] );
	for(int i=0;i<=25;i++)
	{
		if( sum[i]==maxx )
		{
			ans[++tot] = i+'a';
		}
	}
	printf("%c",ans[1]);
	for(int i=2;i<=tot;i++)
	{
		printf(" %c",ans[i]);
	}
}
int main()
{
	solve();
	return 0;
}
C 投票
题目解析

​ 思路很简单,对每个候选人判断即可,注意票数为0与票数相同的情况。

题解代码
#include <stdio.h>
#include <stdlib.h>

int max(int a,int b)
{
    if(a>b)return a;
    else return b;
}
void solve()
{
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    int temp=max(a,max(b,c));
    int base=0;
    if(a==temp)base++;
    if(b==temp)base++;
    if(c==temp)base++;
    if(base==3)
    {
        printf("1 1 1\n");
        return ;
    }
    if(base==2)
    {
        printf("%d %d %d\n",temp-a+1,temp-b+1,temp-c+1);
        return ;
    }
    printf("%d %d %d\n",(temp==a ? 0:temp-a+1),(temp==b ? 0:temp-b+1),(temp==c ? 0:temp-c+1));
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        solve();
    }
    return 0;
}
D 数论
题目解析

​ 可以通过去掉数组中的许多个0其中一个1得到s-1,所以只需要分别计算0和1的数目,记为f0和f1,答案即为 2 f 0 ∗ f 1 2^{f0}*f1 2f0f1

题解代码
#include <stdio.h>
#include <math.h>
int main() {
    int t;
    scanf("%d",&t);
    while (t--) {
        int n, a[100010];
        scanf("%d", &n);
        int f0 = 0;
        int f1 = 0;
        long long sum = 0;
        for (int i = 0; i < n; ++i) {
            scanf("%d", &a[i]);
            sum += a[i];
            if (a[i] == 0)
                f0++;
            if (a[i] == 1)
                f1++;
        }
        printf("%lld\n", (long long) f1 * (long long) pow(2, f0));
    }
    return 0;
}

E 全排列
题目解析

​ 首先考虑最简单的情况,n个数互不相同且非零,答案为 A n n A_n^n Ann,其次考虑有重复数字的情况,举例说明,如1 1 2 3 3 4,答案为 A 6 6 / ( A 2 2 × A 2 2 ) A_6^6/(A^2_2×A_2^2) A66/(A22×A22)其他情况与此类似,最后考虑有0的情况,0不能在首位,故不考虑重复的情况下,有0时答案为(n – 0的个数)× A n − 1 n − 1 A_{n-1}^{n-1} An1n1然后转到第二步即可

故用一个数组记录每个数字出现的个数,按照上面过程模拟即可,标程中已注释

题解代码
#include <stdio.h>
#include <stdlib.h>

int factorial(int n)//求n的阶乘
{
  int ans = 1, i;
  for (i = 1; i <= n; i++) {
    ans *= i;
  }
  return ans;
}

int main()
{
  int num[10] = {0};//表示每个数出现的次数
  int n, i, t, ans = 1;

  scanf("%d", &n);
  for (i = 0; i < n; i++) {
    scanf("%d", &t);
    num[t]++;//出现次数+1
  }

  if (num[0]) {//是否有0
    ans = (n - num[0]) * factorial(n - 1);//有零
  } else {
    ans = factorial(n);//无零
  }

  for (i = 0; i < 10; i++) {
    if (num[i]) {
      ans /= factorial(num[i]);//消去重复的排列
    }
  }

  printf("%d", ans);

  return 0;
}

F 小G的幻想乡
题目解析

40 % 40\% 40%分数解法: 这部分数据 n ≤ 2 n \leq 2 n2,那么就很好分类了
n = = 1 n==1 n==1时,直接输出答案,当 n = = 2 n==2 n==2时,直接输出 max ⁡ ( a + b , a − b ) \max( a+b ,a-b ) max(a+b,ab)

100 % 100\% 100%分数解法: 当我们选好一次操作的 x x x y y y后,会发现
y ≤ 0 , x + y ≥ x − y y ≥ 0 , x + y ≤ x − y y\leq0 , x+y\geq x-y \\ y\geq0,x+y \leq x-y y0,x+yxyy0,x+yxy

那么最后一定是 x + ∣ y ∣ x + \mid y \mid x+y的数值最大,除了第一个数外,每一个数都会有机会成为 x x x y y y中的 y y y。所以答案就是
a n s = a 1 + ∑ i = 2 n ∣ a i ∣ ans = a_1 + \sum_{i=2}^n \mid a_i \mid ans=a1+i=2nai
但是注意数据范围最后会很大,不用long long的话只能拿到90%分。

题解代码

40 % 40\% 40%分数解法:

#include<stdio.h>//40%分数code
#include<math.h>
int x,y,n,ans;
int MAX(int a,int b) {
    if( a>b ) return a;
    else return b;
}
void solve() {
    scanf("%d",&n);
    if( n==1 ) {
        scanf("%d",&x);
        ans = x;
    } else if( n==2 ) {
        scanf("%d %d",&x,&y);
        ans = MAX( x+y, x-y );
    }
    printf("%d",ans);
}
int main() {
    solve();
    return 0;
}

100 % 100\% 100%分数解法:

#include<stdio.h>
#include<math.h>
#define LL long long//最后一个测试点数据量很大,需要long long
LL x,n,ans;
void solve() {
    ans = 0;
    scanf("%lld",&n);
    for(int i=1; i<=n; i++) {
        scanf("%lld",&x);
        if( i==1 ) ans += x;//第一个数字需要特殊判断
        else ans += abs(x);//其余数字直接加绝对值就好
    }
    printf("%lld",ans);
}
int main() {
    solve();
    return 0;
}
G 时空尽头
题目解析

本题主要考察用代码模拟实际问题的能力。对于每个移动方案,先令 c a n = t r u e can=true can=true ,假设它可以满足要求。将移动的操作保存在 o p [ N ] [ 2 ] op[N][2] op[N][2]中; c n t [ i ] ( 1 ≤ i ≤ 3 ) cnt[i](1\le i\le 3) cnt[i](1i3)记录第 i i i根石柱上现在有多少个圆盘; 保存 a [ i ] [ N ] a[i][N] a[i][N]着第 i i i根石柱上所有圆盘的顺序。
初始化令 a [ 1 ] [ i ] = n − i + 1 , c n t [ 1 ] = n , c n t [ 2 ] = c n t [ 3 ] = 0 a[1][i]=n-i+1,cnt[1]=n,cnt[2]=cnt[3]=0 a[1][i]=ni+1,cnt[1]=n,cnt[2]=cnt[3]=0
对于每个从第 f r fr fr根石柱移动到第 t o to to根石柱的操作,若第 f r fr fr根石柱上没有圆盘,那么即发现不合法的操作。
否则,检查第 t o to to根石柱顶端的圆盘是否小于第 f r fr fr根石柱顶端圆盘的大小,若是,则发现不合法。
将第 f r fr fr根石柱顶端圆盘移动到第 t o to to根石柱顶端,修改 c n t [ f r ] cnt[fr] cnt[fr] c n t [ t o ] cnt[to] cnt[to]
检查完所有的移动操作后,若依然没有发现不合法操作,并且此时所有圆盘都在第三根石柱上,那么这个方案就是满足题意的;否则不满足题意。

题解代码
#include<stdio.h>
#include<stdlib.h>
#define N 10010
int T,n,m,a[4][N],cnt[4],op[N][2];
void solve() {
    int can=1;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++)
        scanf("%d%d",&op[i][0],&op[i][1]);
    for(int i=1; i<=n; i++) a[1][i]=n-i+1;
    cnt[1]=n;
    cnt[2]=0;
    cnt[3]=0;
    for(int i=1; i<=m; i++) {
        int fr=op[i][0],to=op[i][1];
        if(cnt[fr]==0) {
            can=0;
            break;
        }
        if(cnt[to]>=1&&a[fr][cnt[fr]]>a[to][cnt[to]]) {
            can=0;
            break;
        }
        a[to][++cnt[to]]=a[fr][cnt[fr]--];
    }
    if(can==1&&cnt[1]==0&&cnt[2]==0&&cnt[3]==n)
        printf("YES\n");
    else printf("NO\n");
}
int main() {
    scanf("%d",&T);
    while(T--) solve();
}
H 小G的硬币
题目解析

​ 首先来探讨一下怎么求 f ( s ) f(s) f(s),用贪心的策略去考虑的话,对于给定的 s s s a a a,从高到低的去遍历 a a a,那么对第 i i i种货币需要的数量为 ⌊ s / 1 0 a i ⌋ \lfloor s/10^{a_i}\rfloor s/10ai( s s s为减去上一个之后剩下的值),显然遍历过第 i i i中货币后, s s s的新值满足 s < 1 0 a i s<10^{a_i} s<10ai,那么 s s s a i − 1 a_{i-1} ai1所取的值不会超过 1 0 a i / 1 0 a i − 1 − 1 10^{a_i}/10^{a_{i-1}}-1 10ai/10ai11,计算结果中如果没有 − 1 -1 1,为了达到最少,就可以直接取 a i a_i ai

那么根据题意来求解 s s s,实际上就是找一个最小的数 s s s使得 f ( s ) > k f(s)>k f(s)>k ,显然 f ( s ) f(s) f(s)的最小值就是 k + 1 k+1 k+1,那么问题转换成用 k + 1 k+1 k+1张货币来构造 s s s了,为了使 s s s的面额最小,对 a a a从小到大遍历,设 n u m num num为剩下的可用支票数(初值为 k + 1 k+1 k+1),对于 a i a_i ai,其选择的数量应为 m i n ( n u m , 1 0 a i / 1 0 a i − 1 − 1 ) min(num,10^{a_i}/10^{a_{i-1}}-1) min(num,10ai/10ai11),前者是所需的最少货币数,后者是最多能拿多少 a i a_i ai,这样就不会破坏 f ( s ) f(s) f(s)的最小值。

题解代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef long long ll;
int t,a[10],n,k;
ll Min(ll a,ll b)
{
    if(a>b)return b;
    else return a;
}
int main() {
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&n,&k);
        int tmp;
        ll res=0;
        for(int i=0; i<n; i++) {
            scanf("%d",&a[i]);
            tmp=1;
            while(a[i]--)tmp*=10;
            a[i]=tmp;
        }
        k++;
        for(int i=0; i<n; i++) {
            tmp=k;
            if(i+1<n)tmp=Min(tmp,a[i+1]/a[i]-1);//最后一个除外,把剩余的都给最后一个
            res+=a[i]*1ll*tmp;
            k-=tmp;
        }
        printf("%lld\n",res);
    }
    return 0;
}
I zjy&xmd&shoes
题目解析

由于在所有种类的鞋子了,只有一种是奇数,其他都是偶数,利用异或自反性可以得到答案。

题解代码
#include <stdio.h>
#define ll long long
signed main() {
	int T,i,n;
	scanf("%d",&T);
	while(T--)
	{
		ll x,ans=0;
		scanf("%d",&n);
		for(i=1;i<=n;i++)  scanf("%lld",&x),ans^=x;
		printf("%lld\n",ans);
	}
	return 0;
}
J De Moivre's formula
题目解析

这道题可以从数或几何的角度去想。

正确的结果是: r 1 / n ( c o s θ + 2 k π n + i s i n θ + 2 k π n ) , k = 0 , … , n − 1 r^{1/n}\big(cos\frac{\theta+2k\pi}{n}+isin\frac{\theta+2k\pi}{n}\big),k=0,\dots,n-1 r1/n(cosnθ+2kπ+isinnθ+2kπ),k=0,,n1

要用极坐标形式下解决这个问题,由于两个复数相乘在复平面上表现为它们的模相乘,幅角相加,即 ( r 1 ( c o s θ 1 + i s i n θ 1 ) ) ( r 2 ( c o s θ 2 + i s i n θ 2 ) ) = r 1 r 2 ( c o s ( θ 1 + θ 2 ) + i s i n ( θ 1 + θ 2 ) ) \big(r_1(cos\theta_1+isin\theta_1)\big)\big(r_2(cos\theta_2+isin\theta_2)\big)=r_1r_2\big(cos(\theta_1+\theta_2)+isin(\theta_1+\theta_2)\big) (r1(cosθ1+isinθ1))(r2(cosθ2+isinθ2))=r1r2(cos(θ1+θ2)+isin(θ1+θ2)),因此在模 r r r上和幅角上的运算是分开的。要求一个数的 n n n次方根,如果依然用这种极坐标的表示法,那么这些根的模一定是 r 1 / n r^{1/n} r1/n(开方运算)。在幅角上,根的幅角应该为 θ + 2 k π n , k = 0 , … , n − 1 \frac{\theta+2k\pi}{n},k=0,\dots,n-1 nθ+2kπ,k=0,,n1(除法运算),正好 n n n个,加 2 k π 2k\pi 2kπ的原因就是这样这些根的 n n n次方辐角为 θ + 2 k π \theta+2k\pi θ+2kπ,而 c o s ( θ + 2 k π ) = θ , s i n cos(\theta+2k\pi)=\theta,sin cos(θ+2kπ)=θ,sin同,它们的 n n n次方的幅角是不变的,也就是同一个复数,而这样不同的根能取 0 , 1 , … , n − 1 0,1,\dots,n-1 0,1,,n1一共 n n n个(若 k = n k=n k=n则与 k = 0 k=0 k=0的副角相同,代表同一个复数),因此让 k k k从0取到n-1顺序循环运算,再换成普通形式就可得到数值解。

题解代码
#include <stdio.h>
#include <math.h>

double Pi=acos(-1);

int main()
{
    double a,b;
    int n;
    while(scanf("%lf%lf%d",&a,&b,&n) && n!=0)
    {
        double module=sqrt(a*a+b*b);
        double r=pow(module,1.0/n);
        double x=acos(a/module);
        for(int k=0;k<n;k++)
        {
            printf("(%.7f)+i(%.7f)\n",r*cos((x+2*k*Pi)/n),r*sin((x+2*k*Pi)/n));
        }
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值