hiho187
每天补补题。。。。
题目1 : 分隔相同整数
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个包含N个整数的数组A。你的任务是将A重新排列,使得任意两个相等的整数在数组中都不相邻。
如果存在多个重排后的数组满足条件,输出字典序最小的数组。
这里字典序最小指:首先尽量使第一个整数最小,其次使第二个整数最小,以此类推。
输入
第一行包含一个整数N,表示数组的长度。(1 <= N <= 100000)
第二行包含N个整数,依次是 A1, A2, … AN。(1 <= Ai <= 1000000000)
输出
输出字典序最小的重排数组。如果这样的数组不存在,输出-1。
样例输入
4
2 1 3 3
样例输出
1 3 2 3
思路 贪心
- 首先可以发现无解的条件: 当某个数字出现的次数超过
(N+1)/2
(
N
+
1
)
/
2
的时候不能满足条件。
1) 预处理 A1 AN A 1 A N ,生成二元组集合 S=(a1,c1),(a2,c2),⋯(am,cm) S = ( a 1 , c 1 ) , ( a 2 , c 2 ) , ⋯ ( a m , c m ) 。表示 a1 a 1 有 c1 c 1 个, a2 a 2 有 c2 c 2 个……, am a m 有 cm c m 个。
2) 找出c值最大的二元组 (ai,ci) ( a i , c i ) 。如果 ci c i 过多,即 ci+ci−1>n c i + c i − 1 > n ,则无解。
3) 依次找出重排后的第一个数、第二个数……第N个数:
3.1) 找出c值最大的二元组 (ai,ci) ( a i , c i ) ,如果ci恰好满足: ci+ci−1==n c i + c i − 1 == n 。那么当前数字只能选择 ai a i 。
3.2) 如果 ci+ci−1<n c i + c i − 1 < n ,找出a值最小的二元组 (ai,ci) ( a i , c i ) 。如果前一个数字选的不是 aj a j ,那么选择 aj a j 。
3.3) 如果前一个数字选的就是 aj a j ,再找出a值次小的二元组 (ak,ck) ( a k , c k ) 。选择 ak a k 。
不论上述哪种情况,假设最后选择的是 (at,ct) ( a t , c t ) ,都要 ct−=1 c t − = 1 。特别的如果 ct=0 c t = 0 ,则要将 (at,ct) ( a t , c t ) 从S中删除。
#include <iostream>
#include <map>
#include <set>
using namespace std;
const int N = 1e5+5;
int a[N];
int main()
{
int n;
cin>>n;
map<int,int>cnt;
set<pair<int,int> > S;
for (int i = 0; i < n; ++i) {
cin>>a[i];
cnt[a[i]]++;
}
for (map<int,int>::iterator it = cnt.begin(); it != cnt.end(); ++it) {
S.insert(make_pair(it->second,it->first));
}
if ((--S.end())->first * 2 - 1 > n) {
cout << "-1" << endl;
return 0;
}
int pre = -1;
for ( int i = 1; i <= n; ++i) {
int x;
if ((--S.end())->first * 2 - 1 == (n + 1 - i)) {
x = (--S.end())->second;
}
else{
map<int,int>::iterator it = cnt.begin();
if (it->first == pre) ++it;
x = it->first;
}
S.erase(make_pair(cnt[x],x));
if (--cnt[x] > 0) {
S.insert(make_pair(cnt[x],x));
}
else
cnt.erase(x);
cout << x << " ";
pre = x;
}
cout << endl;
return 0;
}