Part1. 题目概述
你有一个数组,其顺序可以随意打乱。
你可以在其中任选一个数增加 k,这记为一次操作。
求最小的操作次数以使原数组为回文串。
若不可能,输出 −1。
Part2. 思路
-
首先看到只能增加 k,说明无论怎么操作,每个数除以 k 的余数不变。
-
要求得到回文串且顺序可以随意打乱,也就是说只要满足最终的数组只有不大于一个数出现的次数为奇数(这个数拿一个放中间,其他的左右两边对称放)。
-
再回到1,除以 k 的余数不变,那么可以对除以 k 的每一个余数分组,每一组都可以通过加 k 使得数字相等,比如这组样例的分组:
1 2
| 13 3 2 3 9 14 17 10 22 20 18 30 1 4 28
|
余数为 0 |
余数为 1 |
余数为 2 |
3,9,18,30 |
10,22,1,4,28 |
2,14,17,20 |
-
如何判断是否可行呢?只需要统计所有组中有几组的元素个数为奇数(如上面的分组中余数为 1 组就是奇数个数,必须把这个组中的某个元素放到新序列的中心位置),如果个数大于 1,就一定是不可行的(不可能把多个元素放到中心)。
-
接下来开始构造最优解。可以很清楚地发现,要在每一组中构造出相等的元素对(一个放前面,一个放后面),比如上余数为 1 组可以这样构造:
(2,14),(17,20)
只要:
(2+3×4,14),(17+3×1,20)
进行了 4+1=5 次操作。
如果是奇数个元素的组,只需要取出一个元素放在中心后,变成偶数个元素的组即可。
-
如何使解最优呢?可以先尝试把每一个组内排个序:
余数为 0 |
余数为 1 |
余数为 2 |
3,9,18,30 |
1,4,10,22,28 |
2,14,17,20 |
这时候要分类讨论一下了:
-
一组中有偶数个元素。
从小到大,每次取两个,一定最优。(否则如果跨过中间的元素找,一定会需要更多操作,你可以自己模拟一下)。
-
一组中有奇数个元素。
很快想到朴素方法:枚举要去除的元素,求出去除每个元素下的最优,时间复杂度 O(n2)。
这显然会超时,但大致思路是可行的,求出下一个解时很多计算是和上一个重复的,这就有了优化的方法。下图七个数的情况:
不取 |
第一组 |
第二组 |
第三组 |
1$ |
(2,3) |
(4,5) |
(6,7) |
2$ |
(1,3) |
(4,5) |
(6,7) |
3$ |
(1,2) |
(4,5) |
(6,7) |
4$ |
(1,2) |
(3,5) |
(6,7) |
5$ |
(1,2) |
(3,4) |
(6,7) |
6$ |
(1,2) |
(3,4) |
(5,7) |
7$ |
(1,2) |
(3,4) |
(5,6) |
红色的就是变化的部分,这样就解出了(分奇偶讨论一下)。
f(ai+1)=⎩⎪⎪⎨⎪⎪⎧f(ai)−k(ai+2−ai+1)+k(ai+2−ai),f(ai)−k(ai+1−ai−1)+k(ai−ai−1),i≡1(mod2)i≡0(mod2)
问题解决!
Part3. Code
注意数据范围,存余数等可以开映射解决,也可以离散化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| #include<bits/stdc++.h> using namespace std; int n,a[100001]; int mod[100001]; int k; map<int,int>mds; map<int,int>vis; vector<int>modn; map<int,vector<int> >md; void solve(){ cin>>n>>k; mds.clear(); md.clear(); for(int i=1;i<=n;i++){ cin>>a[i]; mod[i]=a[i]%k; mds[mod[i]]+=1; md[mod[i]].push_back(a[i]); } vis.clear(); modn.clear(); int ss=0; for(int i=1;i<=n;i++){ if(vis[mod[i]]==0){ modn.push_back(mod[i]); if(mds[mod[i]]%2)ss++; vis[mod[i]]=1; } } if(ss>=2){ cout<<-1<<endl; return; } int sum=0; for(auto i:modn){ sort(md[i].begin(),md[i].end()); if(md[i].size()%2){ int ss=0,s=0x3f3f3f3f; for(int j=1;j+1<md[i].size();j+=2){ ss+=(md[i][j+1]-md[i][j])/k; } s=ss; for(int j=0;j+1<md[i].size();j++){ if(j%2){ ss=ss-(md[i][j+1]-md[i][j-1])/k+(md[i][j]-md[i][j-1])/k; } else{ ss=ss-(md[i][j+2]-md[i][j+1])/k+(md[i][j+2]-md[i][j])/k; } s=min(s,ss); } sum+=s; } else{ for(int j=0;j+1<md[i].size();j+=2){ sum+=(md[i][j+1]-md[i][j])/k; } } } cout<<sum<<endl; } int main(){ int t; cin>>t; while(t--)solve(); return 0; }
|