kickstart 2020 G Combination Lock

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 0pi),使得 ∀ r ( 0 ≤ r < p ) \forall r (0\leq r < p) r(0r<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(pqi) 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<bW1),使得 ∀ c ( i < c ≤ b ) \forall c (i< c \leq b) c(i<cb) 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<dW1) 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(0idx<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] (p0)N(p0)V[i]+pre[p1]
  • 在索引 i d x ( p ≤ i d x ≤ i ) idx(p\leq idx \leq i) idx(pidxi)的部分,次数为
    ( i − p + 1 ) ∗ V [ i ] − ( p r e [ i ] − p r e [ p − 1 ] ) (i-p+1)*V[i] - (pre[i]-pre[p-1]) (ip+1)V[i](pre[i]pre[p1])( p p p不为0时)
    ( i − p + 1 ) ∗ V [ i ] − p r e [ i ] (i-p+1)*V[i] - pre[i] (ip+1)V[i]pre[i]( p p p为0时)
  • 在索引 i d x ( i < i d x ≤ b ) idx(i< idx \leq b) idx(i<idxb)的部分,次数为
    ( p r e [ b ] − p r e [ i ] ) − ( b − i ) ∗ V [ i ] (pre[b]-pre[i]) - (b-i)*V[i] (pre[b]pre[i])(bi)V[i]
  • 在索引 i d x ( b < i d x ≤ W − 1 ) idx(b< idx \leq W-1) idx(b<idxW1)的部分,次数为
    ( 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] (W1b)N(pre[W1]pre[b])+(W1b)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";
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值