其他分享
首页 > 其他分享> > CodeTON Round 2 (Div. 1 + Div. 2, Rated, Prizes!) (A——D)

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

作者:互联网

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