C. AquaMoon and Strange Sort
题目链接:https://codeforces.com/contest/1546/problem/C
所用算法:
思维,数组操作,元素交换
题意:
n 个人从左到右站成一排,每个人身前写着一个数字,初始时每个人都面朝右边,每次可以选取任意相邻的两个人,交换他们的位置,每个人在被交换位置时都会改变他面朝的方向(左->右,右->左),现给定初始时每个位置上的人身上的数字,问能否进行若干次操作后使得从左向右每个人身上的数字非降序排列,且每个人面朝的方向都是向右。
t 组测试样例,每次输入人数 n 以及 n 个数(即每个位置的人身上的数字),若能够达到目标则输出"YES",否则输出"NO"。
思路:
由于每次交换只能交换相邻的两个人的位置,且每个人每被交换一次朝向就会改变一次,因此我们来考虑一般的交换操作:
假设我们需要交换 x 和 y,它们之间有 t 个元素,即 \(x\:\overbrace{\cdots\cdots\cdots}^{t个}\:{y}\)
我们先将 x 不断与其右边相邻的元素交换位置,直到其交换到 y 的右边,即 \(\overbrace{\cdots\cdots\cdots}^{t个}\:{y}\:x\),此时 x 被交换了 t+1 次,中间 t 个数以及 y 都被交换了一次,即 \(\underbrace{\overbrace{\cdots\cdots\cdots}^{t个}\:{y}}_{交换次数:1}\:\underset{\underset{交换次数:t+1}{\downarrow{}}}{x}\),再将 y 不断与其左边相邻的元素交换位置,直到其到达原来 x 的位置,即 \({y}\:\overbrace{\cdots\cdots\cdots}^{t个}\:x\),此时 x 和 y 被交换了 t+1 次,中间 t 个元素均被交换了两次,即 \(\underset{\underset{交换次数:t+1}{\downarrow{}}}{{y}}\underbrace{\overbrace{\cdots\cdots\cdots}^{t个}}_{交换次数:2}\underset{\underset{交换次数:t+1}{\downarrow{}}}{x}\)
由此我们可以看出,对于任选两个人交换位置这个操作:
- 对他们之间的人的状态没有任何影响
- 若他们之间的人数为奇数,则他们两个的面朝方向均不变
- 若他们之间的人数为偶数,则他们两个的面朝方向均改变
由间隔人数的奇偶性我们容易想到位置的奇偶性,即若两个人之间有奇数个人,则交换他们的位置之后他们位置的奇偶性不发生改变,而若两个人之间有偶数个人,则交换他们的位置会使他们位置的奇偶性发生改变,因此我们发现一个人在被交换的过程中,其面朝方向改变当且仅当其位置的奇偶性改变(其实不需要考虑这个一般的交换操作也可以想到,因为一个人从当前位置被交换到相邻位置,其位置的奇偶性一定改变了,其朝向也发生了改变,两者一一对应)
因此只需对每个数统计其在原始序列中的奇数和偶数位置各出现了多少次,将序列进行排序后再对每个数统计一次其在奇数和偶数位置各出现了多少次,若两次统计的结果相同则说明能够达到目标,输出"YES",否则输出"NO"
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int a[maxn], odd[maxn], eve[maxn]; //odd[i]表示i在奇数位置出现了多少次,eve[i]表示i在偶数位置出现了多少次
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
memset(odd, 0, sizeof(odd));
memset(eve, 0, sizeof(eve));
int n;
cin >> n;
for (int i = 1; i <= n; i++) //输入数,分别统计每个数在奇数位置和偶数位置出现的次数
{
cin >> a[i];
if (i & 1)
odd[a[i]]++;
else
eve[a[i]]++;
}
sort(a + 1, a + 1 + n); //排序
bool flag = 1;
for (int i = 1; i <= n; i++) //判断排序后每个数在奇数位置和偶数位置出现的次数是否和之前一样
{
if (i & 1)
{
odd[a[i]]--;
if (odd[a[i]] < 0)
{
flag = 0;
break;
}
}
else
{
eve[a[i]]--;
if (eve[a[i]] < 0)
{
flag = 0;
break;
}
}
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
}
总结:
有时脑袋不够灵活,无法按最简单的思路想出解题方法,但是还是可以通过考虑一般的通式、操作等来得到正确的结论