codeforce 609 Div2

本文解析了四道算法竞赛题目,包括寻找两合数之差、数组转换最小代价、求最小美数及非递增序列单位构成。通过深入分析与代码实现,展示了算法设计与优化的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem A

给定一个数,求两个合数,差为这个数。构造方法有很多。

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
 
int judge(int n) {
	if (n < 2) return 0;
	for (int i = 2; i * i <= n; i++) if (n % i == 0)
		return 1;
	return 0;
}
 
int main()
{
	int n;
	scanf("%d", &n);
 
	if (n & 1)
		printf("%d %d\n", 9 + n, 9);
	else
		printf("%d %d\n", 4 + n, 4);
 
	return 0;
}

Problem B

给定两个长度均为n的数组,。以及一个正整数m。

要求找到一个最小的x,使得经过如下运算后,a能经过重新排列等于b。

其中,n的范围是[1,2000],m的范围是[1,10^9]。

 

分析:无需关心如何排列,只要将a变换之后,与b相等的每个值数量也相等即可。

首先想到可以将两个数组重新组合为<值,数量>的形式,之后按值排序。得到代码中的va和vb。

枚举va的第一个元素和vb中的哪个元素对齐,对齐之后x很容易得到,并且相当于将vb当做一个圈,依次和va的每个元素比较是否相等即可。

这里出错在于判断枚举位置是否合法时,只判断了每个单元数量是否相等,而没有变换后值是否相等。

注意:条件一定要仔细分析,写全。

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
 
map<int, int> a, b;
vector<pair<int, int> > va, vb;
vector<pair<int, int> > vc;
 
int main()
{
 
	int n, m;
	scanf("%d%d", &n, &m);
 
	for (int i = 0; i < n; i++) {
		int t;
		scanf("%d", &t);
		if (a.count(t)) a[t]++;
		else a.insert(pair<int, int>(t, 1));
	}
	for (int i = 0; i < n; i++) {
		int t;
		scanf("%d", &t);
		if (b.count(t)) b[t]++;
		else b.insert(pair<int, int>(t, 1));
	}
 
	for (map<int, int>::iterator ite = a.begin(); ite != a.end(); ite++) va.push_back(pair<int, int>(ite->first, ite->second));
	for (map<int, int>::iterator ite = b.begin(); ite != b.end(); ite++) vb.push_back(pair<int, int>(ite->first, ite->second));
	
	int res = 0x7fffffff;
	for (int i = 0; i < vb.size(); i++) {
		bool yes = true;
		int fac = (vb[i].first - va[0].first + m) % m;
		for (int j = 0, k = i; j < va.size(); j++, k = (k + 1) % vb.size()) if(va[j].second!=vb[k].second|| (va[j].first+fac)%m != vb[k].first){
			yes = false;
			break;
		}
		
		
		if (yes) {
			
			res = min(res, fac);
		}
	}
 
	printf("%d\n", res);
	
 
 
	return 0;
}

Problem C

给定一个n位整数,然后给定一个k,并认为如果每一位都满足bi=bi+k,就认为该数字是美的。求大于等于给定数字的最小美数。

n的范围为[2,200000],k的范围为[1,n)。

分析:一开始想到的是,先将数字按bi=bi+k处理,得到的新数字如果已经大于等于原数字,则直接输出即可,并且已经明确数字位数不会发生变化。

如果处理后数字小于原数字,则需要对某些位进行变化。可知我们为了保证数字最小,应该在最低位增加,即如果大于的位置在k位之后,则直接令第k位加1并修改其余位即可,如果在k位之前,则直接令这一位加1并修改其余位即可。

出错的地方在于,一方面错误认为如果小的位数在1-k之间,那么加1并不一定能保证大于原数。可知,前K位和原数相等,若某一位加1,一定大于原数

另一个错误的原因在于,没有处理第k位为9的情况。

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
 
 
char d[200010];
char r[200010];
 
int judge(int size) {
	int pos = 0;
	while (pos < size && d[pos] == r[pos]) pos++;
	if (pos == size) return 0;
	if (d[pos] > r[pos]) return -1 * (pos + 1);
	return 1;
}
 
 
 
int main()
{
 
	int n, k;
	scanf("%d%d", &n, &k); getchar();
	scanf("%s", d);
	int size = strlen(d);
	r[size] = 0;
	for (int i = 0; i < size; i++) r[i] = d[i];
 
	for (int i = k; i < size; i++) r[i] = r[i - k];
 
	int pos = judge(size);
	if (pos < 0) {
		pos = -1 * pos - 1;
		if (pos > k - 1) pos = k - 1;
		while (r[pos] == '9') pos--;
		r[pos++]++;
		while (pos < k) r[pos++] = '0';
		for (int i = k; i < size; i++) r[i] = r[i - k];
	}
 
	printf("%d\n", size);
 
	printf("%s\n", r);
 
 
	return 0;
}

Problem D

给定一个长度为n的非递增序列,表示如下一幅图:

相邻两个可以构成一个单位,求做多可以构成多少个单位。

n的范围为[1,300000]。序列每个元素的范围为[1,300000]

分析:这一题思路很巧妙,用黑白两种颜色给整个图填色,相邻方格不同色即可。对于填好色的图,可知一个单元必定由一黑一白构成,如果黑格数量少,则每个黑格必定可以和一个白格对应,反之亦然,所以,最终变为求白块和黑块的数量,并计算最小值即可。

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
 
int main()
{
 
	int n;
	scanf("%d", &n);
	long long a = 0, b = 0;
	for (int i = 0; i < n; i++) {
		int t;
		scanf("%d", &t);
		if (t & 1) {
			a += t >> 1;
			b += t >> 1;
			if (i & 1) b++;
			else a++;
		}
		else {
			a += t >> 1;
			b += t >> 1;
		}
	}
	
	printf("%lld\n", min(a,b));
 
 
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值