第三届广西大学东信杯题解(感谢中国东信爸爸赞助支持)

引流广西东信杯

“中国东信杯”广西大学第三届程序设计竞赛(新生组)”
https://ac.nowcoder.com/acm/contest/10172?&headNav=www#rank
密码:2020gxuacm19

公共题第一题:A++++

题解:判定na和in的最小值。
如果用char string[200]存的话就很简单了
开两个计数器,读string[0]是i或者n
然后求两个计数器的最小值。总用时八分钟,主要读歪题了
#include<iostream>
#include<stack>
#include<string>
#include<cstring>
#include<algorithm>
//这个是大整数,一般打竞赛long long不容易爆数据int
#define ll long long 
#define N 100000
using namespace std;
int main(){
//关闭流同步,比赛代码,因为cin比scanf慢,会被time limited exceed
     ios::sync_with_stdio(false);
     ll n=0;
     //等效于scanf("%lld",&n);
     cin>>n;
     while(n--){
     	ll t,count1=0,count2=0;
     	cin>>t;
     	while(t--){
		 	char s[40];
		 	cin>>s;
		 	if(s[0]=='i')count1++;
		 	if(s[0]=='n')count2++;
		 }
		 //等效printf("A");
		 cout<<'A';
		 //判定两个计数器哪个数字小,把小的打进count1
		 count1=min(count1,count2);
		 while(count1--){
		 	cout<<'+';
		 }
		 cout<<endl;//回车不能丢
     }
}

第二题:GDP Carry

博弈论,题目和题面毫无干系的题
推理一个结果。已知先手只能走奇数步,后手只能走偶数步
就可以猜测是和奇偶性有关的博弈。我们大胆讨论:
1.如果是总和是奇数,那么龙老板赢定了,因为他一次可以拿完。
2.如果总和是偶数,那么龙老板要怎么样赢呢?偶数=奇数+奇数
3.而一旦出现了奇数,那么光依靠小西肯定是不能使得剩下的步数变成偶数
也就是说,龙老板必然还可以走一步。
总结:
只要步数中有一个奇数步
那么龙老板必然可以走一步
那么小西就必然不能让龙老板在他之前停下。龙老板赢
#include<iostream>
#include<stack>
#include<string>
#include<cstring>
#include<algorithm>
#define ll long long 
#define N 1000005
ll x[N]={};
ll sum[N]={};
bool flag=false;
using namespace std;
int main(){
//关闭流同步
     ios::sync_with_stdio(false);
     ll n=0;
     cin>>n;//等效scanf
     for(ll i=1;i<=n;i++){
     cin>>x[i];
     if(x[i]&1)flag=true;
     //x[i]&1是判定奇偶的快速方法,比较实用,可以加速60倍
     }
     if(flag)cout<<"Antinomy"<<endl;
     //等效prinf("Antinomy");
     else cout<<"XiJam"<<endl;
}

第三题: Interpretability

几何数学题。已知边长为1 2 4 8等比数列下去的边有f1 f2 f3条,求可以组成多少个三角形。
数学有一个三角形的初中结论:a+b>c任意成立。
而在本题中,变成了等腰三角形b>=a,(其中b为腰)
很多人会下意识考虑等边三角形作为第一种情况,其实大错特错。
实际上这题应该把所有等边三角形看成等腰三角形
举个例子1 1 1 6,
如果把6条权值为8的边考虑成等边就会丢掉三条边。
而如果考虑成等腰的话,那么值8的边可以组成三个等腰,和剩下的小边组成等腰三角形。
因此,这题应当优先考虑等腰的边,而不是等边。
#include<iostream>
#include<stack>
#include<string>
#include<cstring>
#include<algorithm>
#define ll long long 
#define N 1000005
ll x[N]={};
ll sum[N]={};
bool flag=false;
using namespace std;
int main(){
     ios::sync_with_stdio(false);
     ll n=0,count1=0;
     ll count2=0;
     ll count3=0;
     cin>>n;
     for(ll i=1;i<=n;i++){
     cin>>x[i];
     //利用迭代循环实现不断判定等腰三角形存在的情况
     while(x[i]>=2&&count2>=1){
     	count2--;
	 	x[i]-=2;
	 	count1++;
		 }
		 //剩下的就变成了底边,真的等边三角形
     count2+=x[i]%3;
     count1+=x[i]/3;
    }
    cout<<count1<<endl;
   
}

第四题 :Batch Normalization 1D

 ~~真实名字:拿头模拟题~~ 
 很漂亮的模拟题,过程其实很清晰,而且题目又长又劝退,题面简单又单纯。可惜我没做对。
 稍微讲一下题意:模拟这样一个过程:
 1.给二维矩阵和一维矩阵的运算模式,利用运算规则进行精度计算
 2.第一步,先求出两个定矩阵
 3.第二步,进入训练状态循环n_b次,将进行两种运算
 其一:求x_hat
 其二:求moving_var和moving_mean,这个运算过程就是模拟过程。
 4.结束:最后模拟运算一次,打印结果。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<cmath>
#include<list>
using namespace std;
long long  n, c,n_b;
double eps, mo;
double  x[205][205] = {};
double x_hat[500][500] = {};
double moving1[205] = {};
double moving2[205] = {};
double beta[20005] = {};
double gamma1[20005] = {};
double out[205][205] = {};
double t[205] = {};
list<string>s1;
double mxxx[50] = {};
double varx[50] = {};
//主代码从这里开始
int main() {
	cin >> n >> c >> n_b >> eps >> mo;
	for (int i = 0;i < n;i++) {
		for (int j = 0;j < c;j++) {
			cin >> x[i][j];//存x矩阵
		}
	}
		for (int i = 0;i < c;i++) {
			cin >> gamma1[i];//存gamma
	}
		for (int i = 0;i < c;i++) {
			cin >> beta[i];//存beta
		}
		for (int i = 0;i < c;i++) {
			cin >> moving1[i];//存moving—mean
		}
		for (int i = 0;i < c;i++) {
			cin >> moving2[i];//存moving-var
		}
	for (int i = 0;i < c;i++) {
		double tmp = 0;
		for (int j = 0;j < n;j++) {
			tmp += x[j][i];
		}
		mxxx[i] = tmp / n;//求mu的矩阵
	}
	for (int i = 0;i < c;i++) {
		double tmp = 0;
		for (int j = 0;j < n;j++) {
			tmp += (x[j][i] - mxxx[i])* (x[j][i] - mxxx[i]);
		}
		varx[i] = tmp / n;//求var的矩阵
	}
	//开始训练
	while (n_b--) {
		for (int i = 0;i < n;i++) {
			for (int j = 0;j < c;j++) {
				t[1] = x[i][j] - mxxx[j];
				t[2] = varx[j] + eps;
				t[2] = sqrt(t[2]);
				x_hat[i][j] = t[1] / t[2];//第一种训练的模拟过程
			}
		}//第二种训练的模拟过程*2
			for (int j = 0;j < c;j++) {
				t[1] = mo * moving1[j];
				t[2] = (1.000000 - mo) * mxxx[j];
				moving1[j] = t[1]+t[2];
			}
			for (int j = 0;j < c;j++) {
				t[1] = mo * moving2[j];
				t[2] = (1.00000 - mo) * varx[j];
				moving2[j] = t[1]+t[2];
			}
	}
	//训练结束,开始最后的运算
	for (int i = 0;i < n;i++) {
		for (int j = 0;j < c;j++) {
			x_hat[i][j] = (x[i][j] - moving1[j]) / sqrt(moving2[j] + eps);
		}
		
	}//打印结果out
	for (int i = 0;i < n;i++) {
		for (int j = 0;j < c;j++) {
			out[i][j] = gamma1[j] * x_hat[i][j] + beta[j];
			printf("%.4f ", out[i][j]);
		}
		if (i != n - 1)cout << endl;
	}
}

新生组第五题:小西和数字转换

思维
倒着思考第x个数以内,除y+1位是1,其余为0
如果不是,那么计数器+1,输出计数器的数字即可。
#include<iostream>
#include<string>
using namespace std;
int main ()
{
	string num;
	int n,x,y;
	cin >> n >> x >> y;
	cin >> num;
	int ans = 0;
	for ( int i = n - x ; i < n; i++ )
	{
//两个判定限制条件,要么倒序y+1是1,其余都不能是1,不满足就扣钱
		if ( ( num[i] == '1' && i != n - y - 1 ) || ( num[i] == '0' && i == n - y - 1 ) )
			ans++;
	}
	cout << ans << endl;
	return 0;
}

新生组第六题:小西和拼图

拼图,简单题
判定两个条件:
其一是m是否会被2整除
其二是是否存在任意拼图第二个数字和第三个数字相同
满足这两个即可。

(据说有一个数据n是读取失败了,有的人正确代码也错了,那么不改变n可能就给过了,可以去试试)

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        int a, b, c, d;
        int k = 0;
       while(n)
        {
            scanf("%d%d%d%d", &a, &b, &c, &d);
            if (b == c)
            {
                k = 1;
            }
           n--;
        }
        if (k == 1 && m % 2 == 0)
            printf("YES\n");
        else  printf("NO\n");
    }
    return 0;
}

新生组第七题:小西和复制粘贴

~~您就是cv工程师?!~~ 
这题是经典的倒叙思维题利用倒序由果导因
已知我们要第i个字母
我们从最后一步往前走走到起点不就知道我们需要第i个字符了么。
//您就是cv工程师??!
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<list>
using namespace std;
long long  n, k,m;
char s[2000005] = {};
long long  x[200005] = {};
long long sum[200006] = {};
long long a[200005] = {};
long long b[200005] = {};
long long c[200005] = {};
list<string>s1;
//刚开始思考用链表然后发现爆了就没写下去
typedef struct node {
	string s;
	node *x;
}T,*tree ;
int main() {
	cin >> k >> m;
	cin >> s;
	cin >> n;
	//倒过来取步骤,方便正向进行,~i就是取反,判定是否i为0
	//这么妖的代码一看就是抄lmgg的
	for (long long  i = n-1;~i;i--) {
		cin >> a[i] >> b[i] >> c[i];
	}
	for (long long i = 0;i < k;i++) {
		long long p = i;//你现在要找第i个数字是哪个字符
		for (long long j = 0;j < n;j++) if(c[j]<=p){
			long long tmp = b[j] - a[j];
			if (c[j] + tmp > p)p = a[j] + p - c[j];
			else p -= tmp;
		}//那就倒退回去,如果比p大了,那么肯定不影响p的生活
		//如果比p小了,要么取代了p,要么让p这个位置往前了。
		//(建议自己手撸一下很快的)
		cout << s[p];
	}
}

新生组防ak: 小西和秀苑酱饼

这题是真的好题,就算放到正式组也是屈指可数的过题量
模拟+dfs,不愧是龙老板
感谢龙老板提供题解!

#include <bits/stdc++.h>

using namespace std;

#define N 510
#define INF 0x3f3f3f3f//最大值

int n, m, num, ans[N][N], vis[N][N];
char s[N][N];

void F(int x, int y, int z)//dfs(深度优先搜索)
{
    if (x < 1 || x > n || y < 1 || y > m || vis[x][y] > 0)
        return;//递归边界,如果出现了如上情况,那么直接终止

    if (vis[x][y] == -1)//如果出现访问过并且未知可不可以走下去的情况,那么标记无穷大,然后终止访问
    {
        num = INF;
        return;
    }
    //标记当前点已经经过但是不知道可不可以走
    vis[x][y] = -1;
    ++num;
//如果这个点是n
    if (s[x][y] == 'N')
    {//z为进来的方向
        if (z == 0 || z == 3)
        {//那么就出去往这两个方向
            F(x, y + 1, 0);
            F(x - 1, y, 3);
        }
        else
        {//那么就往这两个方向走,
            F(x, y - 1, 2);
            F(x + 1, y, 1);
        }
    }
    else
    {//那么进来就是z了
        if (z == 0 || z == 1)
        {//如果z从10进来,就从01出去
            F(x, y + 1, 0);
            F(x + 1, y, 1);
        }
        else
        {//否则就从23出去
            F(x, y - 1, 2);
            F(x - 1, y, 3);
        }
    }
//确实可以走,那么就把访问点标记为可走
    vis[x][y] = 1;
}

int main()
{
    scanf("%d%d", &n, &m);

    for (int i = 1; i <= n; ++i)
        scanf("%s", s[i] + 1);

    memset(ans, 0x3f, sizeof ans);

    for (int i = 1; i <= n; ++i)
    {
        memset(vis, 0, sizeof vis);
        num = 0;

        for (int j = 1; j <= m; ++j)
        {//从四个角落才可以出发,判定四个角落是否满足出发条件
            if (s[i][j] == 'N')
                F(i, j, 1);
            else
                F(i, j, 3);
           //取当前最小值,因为多次访问会出现有大小的情况
            ans[i][j] = min(ans[i][j], num);
        }
    }

    for (int i = 1; i <= n; ++i)
    {
        memset(vis, 0, sizeof vis);
        num = 0;

        for (int j = m; j; --j)
        {
            if (s[i][j] == 'N')
                F(i, j, 3);
            else
                F(i, j, 1);

            ans[i][j] = min(ans[i][j], num);
        }
    }

    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            printf(j == m ? "%d\n" : "%d ", ans[i][j] >= INF ? -1 : ans[i][j] * 2);
            //结果乘2为最终答案
          //你龙老板永远是你龙老板
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值