ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CodeTON Round 2 (Div. 1 + Div. 2, Rated, Prizes!) (A——D)

2022-08-01 17:02:44  阅读:156  来源: 互联网

标签:25 Rated 前缀 int maxv Prizes 数组 Div minv


A. Two 0-1 Sequences

题意:从a数组的第一个和第二个元素中挑选一个元素留下来,另外一个丢掉。可以反复进行此操作。问最终a数组能否等于b数组。
思路:
注意,操作只能在第一个元素和第二个元素中进行。也就是说,一旦能删的删完了,a数组还不能等于b数组,那就是NO。
否则,从后往前看。如果b数组从第m个元素到第2个元素和a数组第n个元素和第n - m + 2个元素完美匹配,就只用看a数组前面有没有等于b数组第一个元素的数,有就是YES,否则NO。
例如

这组数据从第一个数据就不相等,所以不对。

这组数据就可以完美匹配,在a数组的第1~n - m + 1个元素中,也出现了b数组的第一个元素,所以可以。

这组数据在第三个数据处不相等了,所以不对。

代码:

void solve()
{	
	int n, m;
	cin >> n >> m;
	string s, t;
	cin >> s >> t;
	for(int i = n - 1, j = m - 1; i >= 0 && j >= 0; i --, j--)
	{
		if(j != 0 && s[i] != t[j])
		{
			puts("No");
			return ;
		}
		if(j == 0)
		{
			int k = i;
			for(; k >= 0; k --)
			{
				if(s[k] == t[j]) 
				{
					puts("Yes");
					return ;
				}
			}
		}
	}
	
	puts("No");
}

B. Luke is a Foodie

题意:给出一组数据,还有x, v,要求|v - a[i]| <= x, x是固定的,v是任意的,问需要改变多少次v可以使整个数组满足条件。(v的初始化不算次数)
思路:条件完全可以看成一个区间,就是a[i] - x <= v <= x + a[i],而v的区间要尽可能多的覆盖数组中的数。那怎么做呢?就是找到区间中的最大值和最小值。
如果最大值减去最小值小于等于2 * x, 说明可以找到一个v使得条件满足,否则就要改变v的值。此时的最大值和最小值是随着数组下标的递增而变化的。

例如:
12 8
25 3 3 17 8 6 1 16 15 25 17 23

这组样例的x为8,也就是区间最多是16。
第一次遍历:maxv = 25, minv = 25
第二次遍历:maxv = 25, minv = 3,此时maxv - minv = 22明显大于了16,说明无法找到一个v使得25和3在同一区间内,所以ans ++,同时将最大值最小值改为3,也就是从新区间开始
第三次遍历:minv = maxv = 3
第四次遍历:maxv = 17, minv = 3, 17 - 3 = 14 < 16, 合适
……
第七次遍历:maxv = 17, minv = 1
……
第十次遍历:maxv = 25, minv = 1,显然不合适。ans++
……
最后ans = 2,即只需要改变两次v的值,即可满足条件

代码:

void solve()
{	
	int n, x;
	cin >> n >> x;
	int maxv = -INF, minv = INF;
	for(int i = 1; i <= n; i ++)  //这里我预处理了一下,如果区间内的值一开始就可以满足条件就不需要进行下面的遍历了
	{
		cin >> a[i];
		maxv = max(maxv, a[i]);
		minv = min(minv, a[i]);
	}
	
	if(maxv - minv <= 2 * x)
	{
		cout << 0 << endl;
		return ;
	}
	int ans = 0;
	maxv = -INF, minv = INF;
	for(int i = 1; i <= n; i ++)
	{
		maxv = max(a[i], maxv);
		minv = min(a[i], minv);
		if(maxv - minv > 2 * x)
		{
			ans ++;
			maxv = -INF, minv = INF;
			maxv = max(a[i], maxv);
			minv = min(a[i], minv);
		}
	}
	
	cout << ans <<endl;
}
 

C. Virus

题意:一共有n个屋子,序号为1~n,其中m个被感染了。每个被感染的屋子每天都会往其两边扩散病毒。但你可以在他扩散前做一次操作,将一个屋子变成受保护的,后面他将永不被感染。问最后最少会有多少个屋子被感染
思路:将被感染的屋子从低往高排序,然后就是一个区间问题,将每个未被感染的区间取出来,从高往低排序,每次最优的救出屋子,然后记录下来,最后被感染的屋子就是n - ans。
例如:
10 3
3 6 8

其中1,2,3都是未被感染的区间,若我们想将未被感染的区间保护起来,那就需要两次操作,将两边封起来。
例如
3,6,第一天我们将屋子4保护,然后5会被扩散
然后选择6,8。但因为第一天保护了4,7被感染了,那选择8~3,但9,2被感染了。第二天只能选择10或者1,然后其他的就全被感染了。
此时被感染的房屋是8,显然不是最优解。
而如果第一天我们选择保护9,那么2会被扩散,然后保护1,这样9~1中间的10就永不会被感染。再去保护其他区间,可以发现没有可以保护的地方了。
此时被感染房屋是7,显然最优。

代码:

void solve()
{	
	int n, m;
	cin >> n >> m;
	vector<int> v;
	for(int i = 1; i <= m; i ++) cin >> a[i];
	sort(a + 1, a + 1 + m);
	for(int i = 2; i <= m; i ++)
	{
		v.push_back(a[i] - a[i - 1] - 1); // 每个区间可以保护的房屋数量
	}
	
	v.push_back(n - a[m] + a[1] - 1); //首尾
	int cnt = 0, ans = 0;
	sort(v.begin(), v.end(), cmp);  //将区间大小按从大到小排列
	for(int i = 0; i < v.size(); i ++)
	{
		if(v[i] - 1 - cnt > 0) ans += v[i] - 1 - cnt; //如果区间可以围起来,那花两天
		else if(v[i] - cnt > 0) ans += v[i] - cnt; //否则就只救一个屋子
		cnt += 4;  //每花两天,区间内可保护的房屋数量减四
	}
	
	cout << n - ans <<endl;
}

PS:当区间内只能保护一个房屋时,后面的区间肯定一个房屋都保护不了了,所以cnt += 4是没有问题的

D. Magical Array (这道题当时没写出来,所以是补的)

这里我的解法是我的队长翁神的解法,很清晰。
题意:给一个二维数组,和两种操作。
第一种是选两个下标i, j。 然后c[t][i] - 1, c[t][j] - 1, c[t][i-1] + 1, c[t][j + 1] + 1。
第二种是选两个下标i, j。 然后c[k][i] - 1, c[k][j] - 1, c[k][i-1] + 1, c[k][j + 2] + 1。
此处t代表一般数组,k代表特殊数组。
让我们找出k数组的下标和其进行操作二的次数。

思路:对于两种操作,他们操作并不会改变数组数据的总和,但会改变他们的前缀和。
第二种操作的前缀和在前面会变小,原因是它是在j + 2处加一,j + 1 处的前缀和明显也会小于其他进行第一种操作的数组, 而j + 2处的前缀和又和其他数组相同。
于是可以想到。对于前缀和总量特殊数组绝对是最小的。操作次数就是他们前缀和总和的差值。

例如:
3 7
25 15 20 15 25 20 20
26 14 20 14 26 20 20
25 15 20 15 20 20 25
第一组前缀和数组为:
25 40 60 75 100 120 140
第二组
26 40 60 74 100 120 140
第三组
25 40 60 75 95 115 140

很明显可以观察到在75之前他们前缀和之和是一样的,但之后,第三组样例就每次少了5,说明他做了操作二,将这里的数移到了后面,所以这里的前缀和变小了。

void solve()
{	
	int n, m;
	cin >> n >> m;
	vector<PII> v(n + 1);
	for(int i = 1; i <= n; i ++)
	{
		int sum = 0, res = 0;
		for(int j = 1; j <= m; j ++)
		{
			int x;
			cin >> x;
			sum += x; //前缀和
			res += sum; //前缀和总和
		}
		v[i] = {res, i}; //记录总和和其下标
	}
	
	sort(v.begin() + 1, v.end());
	cout << v[1].second << ' ' << v[2].first - v[1].first << endl;
}

另外我看网上其他大佬的解法是(i * c[i])+ ((i + 1) * c[i + 1]) + .... 求出总和。然后找到最小的就是了。
没看懂为什么要乘i,i + 1这种,但思路应该差不多。

标签:25,Rated,前缀,int,maxv,Prizes,数组,Div,minv
来源: https://www.cnblogs.com/lbzbk/p/16540906.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有