Combination Lock
题目链接
题目简介
有 W W W个数字,每个数字都有初始值,且范围在1- N N N,定义一次操作为使 W W W个数字中任意一个数字+1或-1 且 N N N+1定义为1且1-1定义为 N N N,求使得所有 W W W个数均变为同一个数的最小操作次数
解题思路
索引下标均从0开始
- 要想得到最小操作次数,一定先要找到要最终变成哪个数字(记为 X X X)
- 暴力的话就尝试 X X X所有可能的情况(即遍历1- N N N),计算每一次的操作次数,时间复杂度为O( N W NW NW)
- 不难(hennan)发现,要想操作次数最小,一定要使得 X X X在 W W W个数字的初始值之中(具体证明过程见题目链接中的ANALYSIS),这样时间复杂度就会变为O( W 2 W^2 W2))
- 接下来的优化主要在求解每一次的操作次数(定义 i i i为 X X X的索引, V V V数组存储所有 W W W个数字,且为升序排列)
- 维护一个前缀和(定义为数组 p r e pre pre),用来后面计算区间和
- 寻找 p p p( 0 ≤ p ≤ i 0\leq p\leq i 0≤p≤i),使得 ∀ r ( 0 ≤ r < p ) \forall r (0\leq r < p) ∀r(0≤r<p) , r r r与 i i i之间的距离为 N − ( V [ i ] − V [ r ] ) N-(V[i]-V[r]) N−(V[i]−V[r]),使得 ∀ q ( p ≤ q ≤ i ) \forall q(p\leq q\leq i) ∀q(p≤q≤i), q q q与 i i i之间的距离为 V [ i ] − V [ q ] V[i]-V[q] V[i]−V[q]
- 寻找 b b b( i < b ≤ W − 1 i< b\leq W-1 i<b≤W−1),使得 ∀ c ( i < c ≤ b ) \forall c (i< c \leq b) ∀c(i<c≤b) , c c c与 i i i之间的距离为 V [ c ] − V [ i ] V[c]-V[i] V[c]−V[i],使得 ∀ d ( b < d ≤ W − 1 ) \forall d(b< d\leq W-1) ∀d(b<d≤W−1), d d d与 i i i之间的距离为 N − ( V [ d ] − V [ i ] ) N-(V[d]-V[i]) N−(V[d]−V[i])
- 采用二分查找,这样寻找 p p p和 b b b的时间复杂度为O(logW)
- 这样把数组 V V V划分成了4个部分,每个部分计算其中所有的数字变为 X X X的次数可以用公式计算,时间复杂度为O(1)
- 在索引
i
d
x
(
0
≤
i
d
x
<
p
)
idx(0\leq idx < p)
idx(0≤idx<p)的部分(
p
p
p为0时次数为0),次数为
( p − 0 ) ∗ N − ( p − 0 ) ∗ V [ i ] + p r e [ p − 1 ] (p-0)*N - (p-0)*V[i] + pre[p-1] (p−0)∗N−(p−0)∗V[i]+pre[p−1] - 在索引
i
d
x
(
p
≤
i
d
x
≤
i
)
idx(p\leq idx \leq i)
idx(p≤idx≤i)的部分,次数为
( i − p + 1 ) ∗ V [ i ] − ( p r e [ i ] − p r e [ p − 1 ] ) (i-p+1)*V[i] - (pre[i]-pre[p-1]) (i−p+1)∗V[i]−(pre[i]−pre[p−1])( p p p不为0时)
( i − p + 1 ) ∗ V [ i ] − p r e [ i ] (i-p+1)*V[i] - pre[i] (i−p+1)∗V[i]−pre[i]( p p p为0时) - 在索引
i
d
x
(
i
<
i
d
x
≤
b
)
idx(i< idx \leq b)
idx(i<idx≤b)的部分,次数为
( p r e [ b ] − p r e [ i ] ) − ( b − i ) ∗ V [ i ] (pre[b]-pre[i]) - (b-i)*V[i] (pre[b]−pre[i])−(b−i)∗V[i] - 在索引
i
d
x
(
b
<
i
d
x
≤
W
−
1
)
idx(b< idx \leq W-1)
idx(b<idx≤W−1)的部分,次数为
( W − 1 − b ) ∗ N − ( p r e [ W − 1 ] − p r e [ b ] ) + ( W − 1 − b ) ∗ V [ i ] (W-1-b)*N - (pre[W-1] - pre[b]) + (W-1-b) * V[i] (W−1−b)∗N−(pre[W−1]−pre[b])+(W−1−b)∗V[i] - 这样时间复杂度就是O( W l o g W WlogW WlogW)
代码
#include<bits/stdc++.h>
#define MAXN 100010
#define int long long
using namespace std;
int T,W,N,values[MAXN],pre[MAXN],p,b;
int ret,ret_each;
bool test1(int mid,int xi){
if(values[mid]-xi>=-N/2)return 1;
else return 0;
}
bool test2(int mid,int xi){
if(values[mid]-xi<=N/2)return 1;
else return 0;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
for(int g=1;g<=T;g++){
ret = LLONG_MAX;
cin >> W >> N;
for(int i=0;i<W;i++){
cin >> values[i];
}
sort(values,values+W);
pre[0] = values[0];
for(int i=1;i<W;i++){
pre[i] = pre[i-1] + values[i];
}
for(int i=0;i<W;i++){
ret_each = 0;
int l,r,mid;
l = 0;
r = W - 1;
while(l<r){
mid = (l+r)/2;
bool flag = test1(mid,values[i]);
if(flag){
r = mid;
}else{
l = mid + 1;
}
}
p = l;
l = 0;r = W - 1;
while(l<r){
mid = (l+r+1)/2;
bool flag = test2(mid,values[i]);
if(flag){
l = mid;
}else{
r = mid - 1;
}
}
b = l;
if(p!=0)ret_each += (i-p+1)*values[i] - (pre[i]-pre[p-1]);
else ret_each += (i-p+1)*values[i] - pre[i];
ret_each += (pre[b]-pre[i]) - (b-i)*values[i];
if(p!=0)ret_each += (p-0)*N - (p-0)*values[i] + pre[p-1];
ret_each += (W-1-b)*N - (pre[W-1] - pre[b]) + (W-1-b) * values[i];
ret = min(ret,ret_each);
}
cout << "Case #" << g << ": " << ret << "\n";
}
}