程序设计思维与实践 CSP-M2 补题

在这里插入图片描述
在这里插入图片描述


思路:有三种情况

  • 有3种不同的数,如果mid*2=min+max,则成立
  • 有2或1种不同的数,那么成立
  • 数超过3种,不成立
    把不同的数记录在数组a种,每次读入数先判断有没有这个数(用stl find函数),维护数组的index,如果index>4,则意味着有了3种以上不同的数,那么结束。

#include <iostream>
#include <algorithm>
#define ll ll
using namespace std;

ll a[10005];
int main()
{
	int t; cin >> t;
	while (t--)
	{
		bool flag = false;
		int n, index = 1; cin >> n;
		for (int i = 1; i <= n; i++)
		{
			ll temp; cin >> temp;
			if (find(a + 1, a + index, temp) == a + index) a[index++] = temp;
			if (index > 4)
			{
				cout << "NO\n";
				flag = true;
				for (int j = i + 1; j <= n; j++) cin >> a[j];
				break;
			}
		}
		if (flag) continue;
		sort(a + 1, a + 4);
		if (index <= 3 || 2 * a[2] == a[1] + a[3]) cout << "YES\n";
		else cout << "NO\n";
	}
}

Problem B-HRZ学英语

Description
瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想 到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想, 这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字 符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字 母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现 的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果 不存在,输出-1! 这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮 他解决这个问题,报酬是可以帮你打守望先锋。 说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二 个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排 在前。例如

AB??EFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
1
2
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。 注意,题目要求的是 第一个出现的,字典序最小的!

Input
输入只有一行,一个符合题目描述的字符串。

Output
输出只有一行,如果存在这样的子串,请输出,否则输出-1

Samples
Input

ABC??FGHIJK???OPQR?TUVWXY?
Onput
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Input
AABCDEFGHIJKLMNOPQRSTUVW??M
Output
-1
Note
在这里插入图片描述


思路:类似于尺取法,维护一个窗口,没有重复的字母。如果遇到?,continue,遇到字母,判断窗口内是否存在,判断方法是定义一个bool数组exist,如果存在,则调整窗口左端位置,字母所在位置记录在数组position种,调整到重复字母只有一个。如果窗口大小为26,则停止。输出时,如果遇到?,则检索exist数组,输出字典序最小的且不存在的,之后把该元素设置为true.


#include<string>
#include<iostream>
#define ll long long
using namespace std;

bool exist[26] = { false };
ll position[26];

int main()
{
	string str;cin >> str;
	ll l = 0, r = -1, size = str.size();
	for (ll i = 0; i < size; i++)
	{
		if (str[i] == '?')
		{
			if ((i - l + 1) == 26)
			{
				r = i;
				break;
			}
			else continue;
		}

		if (exist[str[i] - 'A'] == true) 
		{
			for (ll m = l; m < position[str[i] - 'A']; m++)
				exist[str[m] - 'A'] = false;
			l = position[str[i] - 'A'] + 1; 
			position[str[i] - 'A'] = i;
		}
		else
		{
			exist[str[i] - 'A'] = true;
			position[str[i] - 'A'] = i;
		}
		if ((i - l + 1) == 26)
		{
			r = i;
			break;
		}
	}
	if (r == -1)
	{
		cout << -1;
	}
	else
	{
		int now = 0;
		for (ll m = l; m <= r; m++)
		{
			if (str[m] != '?') cout << str[m];
			else
			{
				for (int p = now; p < 26; p++)
				{
					if (exist[p] == false)
					{
						cout << (char)(p + 'A');
						now = p + 1;
						break;
					}
				}
			}
		}
	}
}


C-咕咕东的奇妙序列

Description
咕咕东 正在上可怕的复变函数,但对于稳拿A Plus的 咕咕东 来说,她早已不再听课,此时她在睡梦中 突然想到了一个奇怪的无限序列:112123123412345 …这个序列由连续正整数组成的若干部分构成,其 中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所 有数字,第i部分总是包含1至i之间的所有数字。所以,这个序列的前56项会是 11212312341234512345612345671234567812345678912345678910,其中第1项是1,第3项是2,第20项是 5,第38项是2,第56项是0。咕咕东 现在想知道第 k 项数字是多少!但是她睡醒之后发现老师讲的东西 已经听不懂了,因此她把这个任务交给了你。

Input
输入由多行组成。 第一行一个整数q表示有q组询问(1<=q<=500)

接下来第i+1行表示第i个输入ki,表示询问第ki项数字。 1<=ki<=10^18

Output
输出包含q行

第i行输出对询问ki的输出结果。

Sample Input
5
1
3
20
38
56
1
2
3
4
5
Sample Onput
1
2
5
2
0
1
2
3
4
Note
在这里插入图片描述


思路:举个例子,输入数据记为i,就是位置,如果i == 67,那结果就是0

(1 12 123 1234 12345 123456 1234567 12345678 123456789 ) (12345678910
1234567981~~0~~ 11 )👈就是这个0

空格分开的我称之为大区间,先确定大区间中最大数的位数(就是收在那个括号中,括号中的大区间最大位数相同),最大数位数相同的大区间的长度为等差序列,比如对于上面例子来说序列为:
1 2 3 4 5 6 7 8 9 (一位)
11 13 15 17…(二位)
这样的话用等差求和公式来计算卡区间。
这样定位到了二位的大区间,i-=preSum,就是减去一位大区间的总长度,问题变成从

12345678910 1234567891011 …

中找第i位,计算得i == 67-45=22。
接下来确定具体在哪个大区间,在一个括号中的大区间的长度符合等差序列,方便求和。采用二分法来找到大区间——否则会超时——二分的基准是第x个大区间,初始时l == 1,r == 括号中总区间数cnt,cnt在上面步骤中已经求出,比较 i 与 第一个大区间到mid所在大区间的总位数 的大小。
在例子中总长度的序列为:11 11+13, 11+13+15…所以i == 22在11+13的大区间里。i-=preSum,计算得i==11,问题转化为在

1234567891011中找到第11位

先确定i在所在的数字是第几位。。。懒得说了

#include <iostream>
#include <string>
#define ll long long
using namespace std;

ll func(ll i)
{
	ll sum = 0, preSum = 0, index = 0, cnt = 9, a = 1, b = 1;
	while (sum < i)
	{
		preSum = sum;
		sum += (cnt * b + cnt * (cnt - 1) * a / 2);
		b += cnt * a + 1;
		cnt *= 10, a++;
	}
	ll rest = i - preSum;
	a--, cnt /= 10;
	b -= cnt * a + 1;
	ll max = b + (cnt - 1) * a;
	ll r = cnt;
	ll l = 1;
	ll mid = (r + l) / 2;
	while (l < r)
	{
		if (rest < mid * b + mid * (mid - 1) * a / 2)
		{
			r = mid - 1;
			mid = (l + r) / 2;
			continue;
		}
		else if (rest > mid * b + mid * (mid - 1) * a / 2)
		{
			l = mid + 1;
			mid = (l + r) / 2;
			continue;
		}
		else break;
	}
	if (rest > mid* b + mid * (mid - 1) * a / 2) mid++;
	rest -= b * (mid - 1) + (mid - 1) * (mid - 2) * a / 2;
	sum = preSum = cnt = 0;
	cnt = 9;
	/*确定位数*/
	b = 1;//位数
	while (sum < rest)
	{
		preSum = sum;
		sum += cnt * b;
		cnt *= 10;
		b++;
	}
	rest -= preSum;
	b--;
	ll k = pow(10, b - 1);
	a = (rest - 1) / b;
	ll number = k + a;
	rest %= b;
	string s = to_string(number);
	rest = rest == 0 ? b : rest;
	ll p = log10(number);
	ll o = pow(10, p + 2 - rest);
	ll e = pow(10, p + 1 - rest);
	return number % o / e;
}
int main()
{
	int q; cin >> q;
	while (q--)
	{
		ll i; cin >> i;
		cout << func(i) << endl;
	}
}

反思:代码写的挺乱的,因为一直在纠结给变量取名字的问题。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值