IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Deltix Round Summer 2021 (open for everyone rated Div. 1 + Div. 2)B. Take Your Places! 题解 -> 正文阅读

[数据结构与算法]Deltix Round Summer 2021 (open for everyone rated Div. 1 + Div. 2)B. Take Your Places! 题解

题目大意

威廉姆有 n n n个整数的数列 a 1 , a 2 , … a n a_1,a_2,…a_n a1?,a2?,an?,他每次可以交换相邻的两个数。威廉姆想请你计算出他交换的最小次数,使得数列中相邻的元素奇偶性不同。

输入描述

第一行包含一个整数 t ( 1 ≤ t ≤ 1 0 4 ) t(1≤t≤10^4) t(1t104),表示测试用例的组数。每组测试用例的第一行包含一个 n ( 1 ≤ n ≤ 1 0 5 ) n(1≤n≤10^5) n(1n105),表示数列中元素的个数。第二行包含 n n n个整数 a 1 , a 2 , … , a n ( 1 ≤ a i ≤ 1 0 9 ) a_1,a_2,…,a_n(1≤a_i≤10^9) a1?,a2?,,an?(1ai?109)表示威廉姆的数列。保证所有测试用例中 n n n的和不超过 1 0 5 10^5 105

输出描述

对于每组测试用例输出最小的操作次数。如果无论如何都不能达成数列中相邻的元素奇偶性不同,输出 ? 1 -1 ?1

样例输入

5
3
6 6 1
1
9
6
1 1 1 2 2 2
2
8 6
6
6 2 3 4 5 1

样例输出

1
0
3
-1
2

思路

这个题非常不容易想到的地方就是可以直接判断每个位置是奇数还是偶数。
首先,统计奇数的数量和偶数的数量,如果二者之差的绝对值大于1,是一定不可能得到想要的结果的。然后,我们有三种情况,分别是:
( i ) (i) (i)奇数数量比偶数数量多1: a 1 , a 3 , … , a n a_1,a_3,…,a_n a1?,a3?,,an?是奇数, a 2 , a 4 , … , a n ? 1 a_2,a_4,…,a_{n-1} a2?,a4?,,an?1?是偶数。
( i i ) (ii) (ii)偶数数量比奇数数量多1: a 1 , a 3 , … , a n a_1,a_3,…,a_n a1?,a3?,,an?是偶数, a 2 , a 4 , … , a n ? 1 a_2,a_4,…,a_{n-1} a2?,a4?,,an?1?是奇数。
( i i i ) (iii) (iii)奇数数量等于偶数数量,此时又分为两种情况:
① ① a 1 , a 3 , … , a n ? 1 a_1,a_3,…,a_{n-1} a1?,a3?,,an?1?是偶数, a 2 , a 4 , … , a n a_2,a_4,…,a_n a2?,a4?,,an?是奇数。
② ② a 1 , a 3 , … , a n ? 1 a_1,a_3,…,a_{n-1} a1?,a3?,,an?1?是奇数, a 2 , a 4 , … , a n a_2,a_4,…,a_n a2?,a4?,,an?是偶数。
我们无法证明出这两种情况中的哪种是最优情况,因此不能只对其中一种情况求解。事实上,通过枚举一些可能的样例,得到这两种情况之间不存在必定最优情况,需要我们都去判断一遍,求两种情况中步数最小的情况。

我们在每种情况中,逐一判断每一位,如果该位的奇偶性与情况不同,则从该位开始向后寻找我们想要的那个奇数或偶数。整个过程依靠尺取法(双指针)完成。已经交换完成的位置就不用去管了,继续向后判断,整个数组只需要扫描一遍,不会超时。每次交换的时间复杂度为 O ( 1 ) O(1) O(1),给出如下证明:

已经事先将数列变更为只表示奇偶性的 01 01 01数列,设第 i i i位为奇数,但是我们想要这一位是偶数。由于我们向右选取最靠左的偶数,位置为 j j j,因此这个偶数向左直到这个奇数的所有数全部为奇数。由传递性知,交换这两个数只需要 j ? i j-i j?i步,利用 s w a p swap swap函数即可,无需模拟整个交换过程。

考虑贪心的思想。若选第二靠左的偶数,则不能保证传递性。交换后,原位置 ( i + 1 ) (i+1) (i+1) ~ ( j ? 1 ) (j-1) (j?1)之间的数整体右移,下一次再选原先最靠左的偶数,所需步数相比原来一定不是最小步数,故贪心地选取是最优策略。

考点

尺取法
贪心

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int t, n, ans_f, ans_s, ans, a[maxn], b[maxn];
void init() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> t; }
int main()
{
	init();
	while (t--) {
		cin >> n;
		for (int i = 1; i <= n; i++)cin >> a[i], b[i] = a[i];
		int odd = 0, eve = 0, ans_f = 0, ans_s = 0, ans = 0;
		for (int i = 1; i <= n; i++) {
			if (a[i] % 2 == 1)odd++, a[i] = 1, b[i] = 1;
			else eve++, a[i] = 0, b[i] = 0;
		}
		if (abs(odd - eve) > 1) {
			cout << -1 << endl;
			continue;
		}
		if (eve != odd) {
			for (int cur, k = 1, i = 1, j = 1; k < n; k++) {
				if (eve > odd)cur = (k + 1) % 2;
				else if (eve < odd)cur = k % 2;
				if (i <= k) {
					i = k + 1;
					while (a[i] == 0 && i < n)i++;
				}
				if (j <= k) {
					j = k + 1;
					while (a[j] == 1 && j < n)j++;
				}
				if (a[k] != cur) {
					if (cur == 1) {
						swap(a[k], a[i]);
						ans += (i - k);
						while (a[i] == 0 && i < n)i++;
					}
					else if (cur == 0) {
						swap(a[k], a[j]);
						ans += (j - k);
						while (a[j] == 1 && j < n)j++;
					}
				}
			}
			cout << ans << endl;
		}
		else {
			for (int cur, k = 1, i = 1, j = 1; k < n; k++) {
				cur = (k + 1) % 2;
				if (i <= k) {
					i = k + 1;
					while (a[i] == 0 && i < n)i++;
				}
				if (j <= k) {
					j = k + 1;
					while (a[j] == 1 && j < n)j++;
				}
				if (a[k] != cur) {
					if (cur == 1) {
						swap(a[k], a[i]);
						ans_f += (i - k);
						while (a[i] == 0 && i < n)i++;
					}
					else if (cur == 0) {
						swap(a[k], a[j]);
						ans_f += (j - k);
						while (a[j] == 1 && j < n)j++;
					}
				}
			}
			for (int i = 1; i <= n; i++)a[i] = b[i];
			for (int cur, k = 1, i = 1, j = 1; k < n; k++) {
				cur = k % 2;
				if (i <= k) {
					i = k + 1;
					while (a[i] == 0 && i < n)i++;
				}
				if (j <= k) {
					j = k + 1;
					while (a[j] == 1 && j < n)j++;
				}
				if (a[k] != cur) {
					if (cur == 1) {
						swap(a[k], a[i]);
						ans_s += (i - k);
						while (a[i] == 0 && i < n)i++;
					}
					else if (cur == 0) {
						swap(a[k], a[j]);
						ans_s += (j - k);
						while (a[j] == 1 && j < n)j++;
					}
				}
			}
			cout << min(ans_f, ans_s) << endl;
		}
	}
	return 0;
}

心得

这个题考试的时候把我卡了两个多小时,我完全没想到最近B题都做不出了,好歹原来AB题都是稳定出。主要是我看到这个数据范围没有直接去思考如何直接扫描一遍数组解决问题,因此没有想到是用尺取法。考试的时候纯粹是贪心过样例,然后一直WA,比赛后看了题解,由于看得不是很细致,导致前后一共WA了10发才AC。今天调这个题,从上午10点调到中午1点才调对。

以后遇到这类问题,首先看数据范围,考虑是否需要优雅的暴力。其次,不要盲目地猜结论,如果没有办法去证明结论,一定不要对着结论死磕。就比如对于上述第三种情况就需要再分成两种情况比较,我当时就盲目地猜测第一个数是什么奇偶性,那么这个数列就只能按照第一个数是这种奇偶性的情况去排。事实上第一个数的奇偶性不能影响全局,如此多的数,当然不可能全部由第一个数去决定后面的奇偶性,后面情况复杂了,会有多种变化。

总之,这次比赛体验很糟糕,下次比赛再努力吧,希望不会犯同样的错误,能够对结论的推导与选择收放自如。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-31 15:42:15  更:2021-08-31 15:44:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:31:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码