2021HAUT第五周新生赛题解

目录

A.爱因斯坦光电效应

B.图书馆信息管理系统

C.这年头难道有人不喜欢签到吗

D.lsjp和他的小伙伴们

E.lwjq与国际象棋

F.lzlf的三角形绝技

G.lwyj的最短路径

H.cgg学长的致富之路

A.爱因斯坦光电效应

    思路:本题给出五个用字符串表示的时间段,需要分别对五个时间段进行处理然后算出五个时间段用秒表示的时间然后存储一下五个时间段的值,之后再循环模拟出所有情况的答案看是否有匹配的答案就可以了,如果有输出Yes,如果没有符合的就输出No。

#include<stdio.h>
int v[30], a, b, c, x, y, z;
int main()
{
	for (int i = 1; i <= 5; i++) //存储每个时间段用秒表示的总时间
	{
		scanf("%d:%d:%d %d:%d:%d", &a, &b, &c, &x, &y, &z);
		int start, end;
		end = x * 3600 + y * 60 + z;
		start = a * 3600 + b * 60 + c;
		v[i] = end - start;
	}
	int ans, f = 0;
	scanf("%d", &ans);
	for (int i = 0; i <= 1; i++)       //判断第一个时间段选不选
	{
		for (int j = 0; j <= 1; j++)    //判断第二个时间段选不选
		{
			for (int k = 0; k <= 1; k++)  //判断第三个时间段选不选
			{
				for (int q = 0; q <= 1; q++) //判断第四个时间段选不选
				{
					for (int w = 0; w <= 1; w++) //判断第五个时间段选不选
					{
						int res = 0;
						if (i == 1) res += v[1];
						if (j == 1) res += v[2];
						if (k == 1) res += v[3];
						if (q == 1) res += v[4];
						if (w == 1) res += v[5];
						if (res == ans)    //有符合的方案
						{
							f = 1; printf("Yes\n"); return 0;
						}
					}
				}
			}
		}
	}
	if (!f) printf("No\n");  //没有符合的方案
}

B.图书馆信息管理系统

思路:这道题目的意思是要判断第一行的字符串能不能通过删除一些子串得到第二个字符串,本质理论是判断第二个字符串的所有元素是否可以按顺序出现在第一个串中即可。

具体实现:循环+双指针维持即可。参考代码如下:

#include<stdio.h>
#include<string.h>
char a[30], b[30];
int main()
{
	gets(a);
	gets(b);
	if (strlen(b) > strlen(a))     //b的长度比a的长直接输出NO
	{
		puts("NO"); return 0;      
	}
	else
	{
		int j = 0;
		for (int i = 0;i < strlen(a);i++)    //判断b字符串中的字符是否按顺序出现在a中
		{
			if (a[i] == b[j])
			{
				j++;
			}
		}
		if (j == strlen(b))    //符合
		{
			puts("YES");
		}
		else                  //不符合
		{
			puts("NO");
		}
	}
}

C.这年头难道有人不喜欢签到吗

题意分析:这道题相当于是数字组合的问题,数字范围是1e9,那么我们可以先把该范围的所有符合的数字先提前预处理存一下,方便程序的实现。

具体分析:接下来的实现就是得到一个正整数n,首先先加上比n的位数少的符合的数字的个数,然后再对n所在的位数符合的数字进行判断,判断该问题时,我们可以从头往后处理,模拟处理。

#include<stdio.h>
#include<string.h>
#include<math.h>
char a[20];
int v[100];
signed main()
{
	int sum = 9;       //数据范围最大1e9;
	int x = 1, xx = 0;
	while (sum--)      //预处理存储相应位数数字的所有可能的情况
	{
		v[xx++] = x; x *= 2;
	}
	gets(a);
	int pd = a[0] - '0';
	if (pd > 1)              //首位数字大于1的时候则可以生成该位数所有的情况
	{
		int ans = 0;
		for (int i = 0;i < strlen(a);i++)
		{
			ans += v[i];
		}
		printf("%d\n", ans);
	}
	else                    //首位数字等于1
	{
		int ans = 0;
		for (int i = 0;i < strlen(a) - 1;i++)   //生成比该数字位数少1的所有情况
		{
			ans += v[i];
		}
		int flag = -1, xx, pd = 0;
		for (int i = 1;i < strlen(a);i++)       //从第一位数字开始处理当前位数的数字
		{
			xx = a[i] - '0';
			if (xx > 1)             //若处理位大于1则后续数字均可以由0或1代替,排列组合思想,总情况就是pow(2, strlen(a) - i)
			{
				pd = 1;
				ans += pow(2, strlen(a) - i); flag = 1; break;
			}
			else if (xx == 1)      //若处理位等于1则当前位不能替换但后续位的数字可以替换为0或1,总情况就是 pow(2, strlen(a) - i - 1)
			{
				ans += pow(2, strlen(a) - i - 1); flag = 1;
			}
		}
		if (flag == -1 || !pd)  //特判输入数字第一位是1需要加上它本身;  特判输入数字为0
		{
			ans++;
		}
		printf("%d\n", ans);
	}
}

lcl大佬的暴力代码:

#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int t = 1;
    while (t--)
    {
        int n;
        cin >> n;
        int ans = 0;
        for (int i = 1; i <= n; i+=10)   //暴力判断以1为最后一位的数中符合条件的数量
        {
            int x = i;
            int ok = 1;
            while(x&&ok)
            {
                if(x%10>1)
                    ok = 0;
                x /= 10;
            }
            ans += ok;
        }
        for (int i = 10; i <= n; i+=10) //暴力判断以0为最后一位的数中符合条件的数量
        {
            int x = i;
            int ok = 1;
            while(x&&ok)
            {
                if(x%10>1)
                    ok = 0;
                x /= 10;
            }
            ans += ok;
        }
        cout << ans << endl;
    }
}

sjp大佬写法,采用位运算(感兴趣的了解):

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
int n, ans;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n;
	if(n==1e9) ans++;   //等于1e9时的特判
	for (int i = 1; i < (1 << 9); i++)  //计算一共2的9次方
	{
		int k = 0;
		for (int j = 0; j < 9; j++)       //将二进制转成十进制数
			k = k * 10 + ((i >> j) & 1);
		if (k <= n) ans++;    //判断该数字与n的大小关系
	}
	cout << ans;
	return 0;
}

 哲佬的数位dp(感兴趣的了解):

#include <bits/stdc++.h>
using namespace std;

int n;
int dp[15],a[15];

int dfs(int pos,bool limit)
{
	if(pos==0) return 1;
    if(!limit&&dp[pos]!=-1) return dp[pos];
    int binary=limit?min(a[pos],1):1,cnt=0;

    for(int i=0;i<=binary;i++)
    {
    	cnt+=dfs(pos-1,limit&&i==a[pos]);
    }

    if(!limit) dp[pos]=cnt;
    return cnt;
}

int solve(int x)
{
	int pos=0;
	while(x)
	{
		a[++pos]=x%10;
		x/=10;
	}
	return dfs(pos,true);
}

int main()
{
	memset(dp,-1,sizeof(dp));
	scanf("%d",&n);
	printf("%d\n",solve(n)-1);
	return 0;
}

D.lsjp和他的小伙伴们

题意理解:这道题目主要就是概率和最大公约数的相关知识点,只要找出大于等于前两位点数的情况再约分即可。

#include<stdio.h>
int gcd(int x, int y)         //最大公约数用于约分
{
	if (x%y == 0) return y;
	else return gcd(y, x%y);
}
int main()
{
	int a, b; scanf("%d %d", &a, &b);
	int maxx = a > b ? a : b;   //求a,b中的最大的那个
	int ans = 6 - maxx + 1;
	int x = gcd(ans, 6);
	printf("%d/%d\n", ans / x, 6 / x);
}

E.lwjq与国际象棋

 思路:先理解下题意,这道题同样是给了一个类似于棋盘的模型,但该棋盘是8*8限定棋盘,每次操作是可以让一行全变为黑色,或者让一列全变为黑色,最终让统计所做的操作数。那么既然每次可以影响一行或者一列,那么我们只需判断第一行的元素和判断第一列的元素即可。

   具体分析:也就是我们分别判断第一行的每个元素,判断的实现是判断该元素所在的列是否全染色,同样分别判断第一列的每个元素的时候的实现也是同样的思路。但要注意的是要特判一下整个棋牌都被染成黑色的情况,不然上述的模拟会多加答案的数目。ok,分析完这些直接上代码:

#include<stdio.h>
char a[10][10];
int main()
{
	int pd = 1;
	for (int i = 1;i <= 8;i++)
	{
		for (int j = 1;j <= 8;j++)
		{
			scanf(" %c", &a[i][j]);
			if (a[i][j] != 'B')
			{
				pd = 0;
			}
		}
	}
	if (pd)        //判断所有字符全为B
	{
		printf("8"); return 0;
	}
	int ans = 0;
	for (int j = 1;j <= 8;j++) 
	{
		if (a[1][j] == 'B')//判断第一行字符如果有B则判断该B所在的列
		{
			int pd = 1;
			for (int k = 1;k <= 8;k++)
			{
				if (a[k][j] != 'B')      //有不符合的情况
				{
					pd = 0; break;
				}
			}
			if (pd) ans++;
		}
	}
	for (int i = 1;i <= 8;i++)
	{
		if (a[i][1] == 'B') //判断第一列字符如果有B则判断该B所在的行
		{
			int pd = 1;
			for (int k = 1;k <= 8;k++)
			{
				if (a[i][k] != 'B')   //有不符合的情况
				{
					pd = 0; break;
				}
			}
			if (pd) ans++;
		}
	}
	printf("%d\n", ans);
}

F.lzlf的三角形绝技

思路:这道题目给出了退化三角形的定义,也就是一个角度是180度或者两个角度都是90度,面积为0的三角形,关于角度的处理是不太方便的,那么我们可以进行转化,也就是说,一个角度是180度意思是有两边之和等于第三边,相当于是等腰三角形腰边和底边重合了,两个角度都是90度的可以转化为有一条边是0;有了这个思路之后我们就可以按照题目给的数据进行判断了。参考代码如下:

#include<stdio.h>
int main()
{
	int a, b, c, d;
	scanf("%d %d %d %d", &a, &b, &c, &d);
	int sum = 0; //判断有多少个0
	if (a == 0) sum++;
	if (b == 0) sum++;
	if (c == 0) sum++;
	if (d == 0) sum++;
	if ((a + b > c) && (a + c > b) && (b + c > a) || (a + b > d) && (a + d > b) && (b + d > a) || (a + c > d) && (a + d > c) && (c + d > a) || (b + c > d) && (b + d > c) && (c + d > b))
	{
		puts("TRIANGLE"); //判断可以组成三角形
	}
	else if (((a + b == c) || (a + c == b) || (b + c == a) || (a + b == d) || (a + d == b) || (b + d == a) || (a + c == d) || (a + d == c) || (c + d == a) || (b + c == d) || (b + d == c) || (c + d == b)) || (sum > 0 && sum <= 2))
	{
		puts("SEGMENT"); //判断可以组成退化三角形:两边相加等于第三边或有0边
	}
	else
	{
		puts("IMPOSSIBLE");
	}
}

G.lwyj的最短路径

    思路:本题采用贪心的思想,也就是说可以斜着走就斜着走,因为这样其实是一步走两个格子,当不能斜着走的时候再水平移动或者竖直移动。

    具体分析:首先要明确一点,从起点到终点本质上的区别是竖直方向距离和水平方向距离的区别,那么走一步产生的影响有三种,水平方向间距减少1,竖直方向间距减少1,水平方向和竖直方向的间距同时减少1。那么很显然最少的操作数就是水平方向间距的差值和竖直方向间距的差值的最大值(由起点到终点的最理想状态),分析完了这个之后我们可以直接写代码了。

第一种写法:从起点到终点的横纵坐标的差值有四种情况(根据正负的组合即可得出有四种),那么我们可以分开讨论再模拟走的过程并输出相应的操作即可:

#include<stdio.h>
#include<math.h> 
int main()
{
	char x; int y;
	char a; int b;
	scanf("%c%d %c%d", &x, &y, &a, &b);
	int ans1 = a - x, ans2 = b - y;   
	printf("%d\n", abs(ans1) > abs(ans2) ? abs(ans1) : abs(ans2));  //最短路径
	if (ans1 >= 0 && ans2 <= 0)   //需要往右下走
	{
		while (ans1 > 0 && ans2 < 0)
		{
			ans1--; ans2++; puts("RD");
		}
		if (ans1 == 0)
		{
			while (ans2 < 0)
			{
				ans2++; puts("D");
			}
		}
		else if (ans2 == 0)
		{
			while (ans1 > 0)
			{
				ans1--; puts("R");
			}
		}
	}
	else if (ans1 >= 0 && ans2 >= 0)  //需要往右上走
	{
		while (ans1 > 0 && ans2 > 0)
		{
			ans1--; ans2--; puts("RU");
		}
		if (ans1 == 0)
		{
			while (ans2 > 0)
			{
				ans2--; puts("U");
			}
		}
		else if (ans2 == 0)
		{
			while (ans1 > 0)
			{
				ans1--; puts("R");
			}
		}
	}
	else if (ans1 <= 0 && ans2 >= 0) //需要往左上走
	{
		while (ans1 < 0 && ans2>0)
		{
			ans1++; ans2--; puts("LU");
		}
		if (ans1 == 0)
		{
			while (ans2 > 0)
			{
				ans2--; puts("U");
			}
		}
		else if (ans2 == 0)
		{
			while (ans1 < 0)
			{
				ans1++; puts("L");
			}
		}
	}
	else if (ans1 <= 0 && ans2 <= 0)  //需要往左下走
	{
		while (ans1 < 0 && ans2 < 0)
		{
			ans1++; ans2++; puts("LD");
		}
		if (ans1 == 0)
		{
			while (ans2 < 0)
			{
				ans2++; puts("D");
			}
		}
		else if (ans2 == 0)
		{
			while (ans1 < 0)
			{
				ans1++; puts("L");
			}
		}
	}
}

下面附上sjp大佬写法:

#include<math.h>
#include<stdio.h>
int h, l, x, y;
char hh[2] = { 'D', 'U' }, ll[2] = { 'L', 'R' };
char a[2], b[2];
int max(int a, int b)
{
	if (a > b) return a;
	else return b;
}
int min(int a, int b)
{
	if (a < b) return a;
	else return b;
}
int main()
{
	scanf("%s", a);
	scanf("%s", b);
	l = b[0] - a[0]; h = b[1] - a[1];  //计算两个点横竖位的差值
	//通过位置差确定应该往哪个方向走
	x = h >= 0 ? 1 : 0;  //x对应hh数组中的方向字母序号
	y = l >= 0 ? 1 : 0;  //y对应ll数组中的方向字母序号
	h = abs(h); l = abs(l);
	printf("%d\n",max(h, l));   //走的总步数就是横竖方向差值的较大那个
	for (int i = 1; i <= min(h, l); i++)   //先斜着走
		printf("%c%c\n", ll[y], hh[x]);
	if (h > l)    //最后分类判断是横向和竖向哪个方向需要走更多步数
	{
		for (int i = 1; i <= h - l; i++)
			printf("%c\n", hh[x]);
	}
	else
	{
		for (int i = 1; i <= l - h; i++)
			printf("%c\n", ll[y]);
	}
	return 0;
}

H.cgg学长的致富之路

思路:这道题的要求已经比较明确了,就是找最优的财富方案所得到的财富的总数,需要特殊注意的一点就是袋子都是相同的,那么我们只需要用现有的袋子优先去装金钱多的袋子即可。

具体分析:对金库每个袋子里面金币数量进行一个降序排序再去判断就可以了。参考代码如下:

写法1 暴力模拟:

#include<stdio.h>
int n,m,ans,res,xh,a[25],b[25],v[25];
int main()
{
	int i,j=0;
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=m;i++)
		scanf("%lld%lld",&a[i],&b[i]);
	res=n;
	while(res>0)   //背包没有空间了即可退出循环
	{
		int num=-1,mx=-1;   //找出当前还未被取走的最大值
		for(i=1;i<=m;i++)
		{
			if(!v[i]&&b[i]>mx)
			{
				mx=b[i];
				num=i;
			}
		}
		if(num==-1) break;   //如果还是-1说明所有物品已被取完,即可直接退出循环
		v[num]=1;    //将该物品标为已取
		if(res>a[num])    //将当前价值最大的物品放入背包
		{
			res-=a[num];
			ans+=a[num]*b[num];
		}
		else
		{
			ans+=res*b[num];
			res=0;
		}
	}
	printf("%lld",ans);
	return 0;
}

 写法2:

#include<algorithm>
#include<iostream>
using namespace std;
typedef struct jg
{
	int first, second;
}ff;
ff a[50];
bool cmp(ff q, ff w)
{
	return q.second > w.second;
}
int main()
{
	int n, m, x, y;
	scanf("%d %d", &n, &m);
	for (int i = 1;i <= m;i++)
	{
		scanf("%d %d", &a[i].first, &a[i].second);
	}
	sort(a + 1, a + m + 1, cmp);     //使用sort
	int ans = 0;
	for (int i = 1;i <= m;i++)
	{
		if (n >= a[i].first)
		{
			ans += a[i].first*a[i].second; n -= a[i].first;
		}
		else
		{
			ans += n * a[i].second; break;
		}
	}
	printf("%d\n", ans);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值