Codeforces Round #792 A-E题解

个人思路,仅作记录,可以参考,欢迎交流。

比赛地址:传送门


A. Digit Minimization

传送门

【题意】给定一个十进制不含0的正整数n和一种操作:先任意选择n的两位调换位置,再删去n的最后一位。求不断进行该操作直到n只剩一位数时这一位数最小是多少

【思路】通过调换尽量将所有位数字中的最小数字留下。可以证明当n的位数≥3时最小数字一定能留下,因为只要在n还剩3位时将最小数字换到第二位,在n还剩2位时将最小数字换到第一位即可。而当n=2时,留下的数一定是初始时的第一位。

【代码】

void solve()
{
	int n;
	cin >> n;
	vector<int> v;
	while (n)
	{
		v.push_back(n % 10);
		n /= 10;
	}
	if (v.size() == 2)
	{
		cout << v[0] << '\n';
		return;
	}
	cout << *min_element(v.begin(), v.end()) << '\n';
	return;
}

B. Z mod X = C

传送门

【题意】给定满足a<b<c的正整数a,b,c,求一组正整数x,y,z,使得x%y=a, y%z=b, z%x=c

【思路】x,y,z在三个关系式中互相制约,环环相扣,难以确定。分析各数间的大小关系,发现需要y>a, z>b, x>c。

设想x>y>z=c>b>a的情况:此时x只受到第一个等式的限制,这样就可以轻易推得一组满足要求的x和y了,即y=z+b,x=y+a。

【代码】

void solve()
{
	long long a, b, c;
	cin >> a >> b >> c;
	long long z = c;
	long long y = z + b;
	long long x = y + a;
	cout << x << " " << y << " " << z << " \n";
	return;
}

C. Column Swapping

传送门

【题意】给定一个n行m列的矩阵,求在最多交换一次矩阵任意两列的情况下,交换哪两列后矩阵可以满足每一行都为非减序列。

【思路】如果答案存在,矩阵的每一行一定只有02个数与矩阵排序后的样子不同。其中,若每一行都是排好序的,则答案直接为”1 1”;而若某一行有2个数不同,则这两个数所在的列就是唯一的可能的答案

所以可以将每一行与其排序好的样子进行对比,找到一组不同的2列,并检查将这两列交换后整个矩阵是否是排好序的。

【代码】

vector<int> grid[200005];
vector<int> comp;
int n, m;

void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)
	{
		grid[i].clear();
	}
	int t;
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 1; j <= m; ++j)
		{
			cin >> t;
			grid[i].push_back(t);
		}
	}
	if (m == 1)
	{
		cout << "1 1\n";
		return;
	}
	bool sorted = 1;
	int lef = -1, rig = -1, cnt = 0;
	for (int i = 1; i <= n; ++i)
	{
		comp = grid[i];
		sort(comp.begin(), comp.end());
		for (int j = 0; j < m; ++j)
		{
			if (grid[i][j] != comp[j])
			{
				if (cnt == 0)
				{
					lef = j;
					cnt++;
				}
				else if (cnt == 1)
				{
					rig = j;
					cnt++;
				}
				else
				{
					cnt++;
					break;
				}
			}
		}
		if (cnt)
		{
			sorted = 0;
		}
		if (cnt == 2)
		{
			break;
		}
		lef = rig = -1;
		cnt = 0;
	}
	if (sorted)
	{
		cout << "1 1\n";
	}
	else if (lef >= 0 && rig >= 0)
	{
		for (int i = 1; i <= n; ++i)
		{
			swap(grid[i][lef], grid[i][rig]);
			if (is_sorted(grid[i].begin(), grid[i].end()) == 0)
			{
				cout << "-1\n";
				return;
			}
		}
		cout << lef + 1 << " " << rig + 1 << '\n';
	}
	else
	{
		cout << "-1\n";
	}
	return;
}

D. Traps

传送门

【题意】你需要经过n个陷阱,已知经过每个陷阱会受到a[i]伤害。你最多能跳过k个陷阱,但每跳过一次会使后续的陷阱伤害都+1。求你全程受到的伤害的最小值。

【思路】先不考虑后续陷阱中也有陷阱被跳过的情况,此时跳过每个陷阱能获得的收益为a[i]-(n-i);再考虑跳过每个陷阱对跳过其他陷阱时伤害增加的缓解:在已经跳过m个陷阱的情况下,再跳过一个陷阱会让所有陷阱造成的伤害增加减少m,即有额外收益m。

所以做法为:先算出每个陷阱的基本收益a[i]-(n-i)并从大到小排序,然后逐个判断a[i]-(n-i)+m≥0是否成立,只要成立就选择跳过,这样就可以算得答案。

【代码】

long long a[200005];

void solve()
{
	long long sum = 0;
	int n, k;
	cin >> n >> k;
	for (int i = 1; i <= n; ++i)
	{
		cin >> a[i];
		sum += a[i];
		a[i] -= n - i;
	}
	sort(a + 1, a + 1 + n, greater<long long>());
	for (int i = 1; i <= k; ++i)
	{
		if (a[i] > -i)
		{
			sum -= a[i] + i - 1;
		}
		else
		{
			break;
		}
	}
	cout << sum << '\n';
	return;
}

E. MEX vs DIFF

传送门

【题意】给定长度为n的数组a,定义DIFF为数组中数值的种数,MEX为数组中最小的不存在的数。在最多能将a中k个数变为任意数的情况下,求DIFF-MEX的最小值

【思路】要想DIFF-MEX最小,就要DIFF尽量小,MEX尽量大。也就是要使a中数值多样性尽量降低,同时要尽量补全较小的不存在的数。分类讨论每种情况下的操作,发现只要是使MEX增加的操作对答案的贡献一定是非负面的,且增加MEX的代价一定不高于减少DIFF所以只要算出k次操作能获得的最大MEX,并求此时最小的DIFF。

具体做法为:将a排序后从小到大遍历,找到填补k次能得到的最大MEX,然后统计比MEX大的数,找出这些数中出现频率最低的数,算出将k次操作用在它们身上最多让DIFF减小多少。最后答案正好为删去k个数后这些数中的种类数的最小值。细节见代码(虽然可读性有点差但我懒得再打文字了)。

【代码】

int n, k, a[100005] = { -1 };//a[0]设定为-1很重要
map<int, int> M;
vector<int> v;

void AddMap(int num)
{
	if (M.find(num) == M.end())
	{
		M.insert({ num, 1 });
	}
	else
	{
		M[num]++;
	}
	return;
}

void solve()
{
	M.clear();
	v.clear();
	cin >> n >> k;
	for (int i = 1; i <= n; ++i)
	{
		cin >> a[i];
	}
	sort(a + 1, a + 1 + n);
	//k次全花完一定没错
	int quota = k;
	int m = 0;
	for (int i = 1; i <= n; ++i)
	{
		if (quota == 0 && a[i] > m)
		{
			AddMap(a[i]);
		}
		else if (a[i] > a[i - 1])
		{
			if (quota >= a[i] - a[i - 1] - 1)
			{
				quota -= a[i] - a[i - 1] - 1;
				m = a[i] + 1;
			}
			else
			{
				m += quota;
				quota = 0; 
				AddMap(a[i]);
			}
		}
	}
	for (auto it = M.begin(); it != M.end(); ++it)
	{
		v.push_back(it->second);
	}
	sort(v.begin(), v.end());
	quota = k;
	int cnt = v.size();
	for (auto it = v.begin(); it != v.end(); ++it)
	{
		if (quota >= *it)
		{
			quota -= *it;
			cnt--;
		}
		else
		{
			break;
		}
	}
	cout << cnt << '\n';
	return;
}

...

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值