资讯详情

秋招笔试算法题——电容充电

秋季笔试算法-电容充电

牛客网《2019年笔试真题精选》 2018年秋季字节跳动笔试题4

主题描述有一个由电容组成的计算器,每个电容组件都有最大容量值(正整数)。 对于单个电容器,操作说明如下: 指令1:放电操作-清除电容器当前电量值 指令2:充电操作-将当前电容补充到最大容量值 两个电容A和B,操作说明如下: 指令3:转移操作-从A中尽可能多地转移电量B,转移不会有电量损失。如果能充满B的最大容量,剩余电量仍将留在A中

现在已知有两个电容,最大容量是a和b其初始状态为0,希望通过一系列操作,使其中一个电容器无论哪一个)中的电量值等于0c(c也是正整数),这一系列操作中使用的正整数)M,无论如何操作,都不可能完成,此时定义M=0。 显然,每组都确定了a、b、c,会有一个M对应。

每组试样的输入格式为: 第一行是正整数N 从第二行开始,每行由三个正整数组成a、b、c,一共有N组 输出为N行,每行打印每组对应M 数据范围: N: 0 &lt; N &lt; 100 0 &lt; N &lt; 100 0<N<100 a、b、c: 0 &lt; a 、 b 、 c &lt; 1 0 5 ( 50 % ) 0 &lt; a、b、c &lt; 10^5(50\%) 0<a、b、c<105(50%) 0 &lt; a 、 b 、 c &lt; 1 0 7 ( 30 % ) 0 &lt; a、b、c &lt; 10^7(30\%) 0<a、b、c<107(30%) 0 &lt; a 、 b 、 c &lt; 1 0 9 ( 20 % ) 0 &lt; a、b、c &lt; 10^9(20\%) 0<a、b、c<109(20%)

2
3 4 2
2 3 4

4
0

对于(3,4,2),最少只需要4个指令即可完成: (设最大容量为3的是A号电容,另一个是B号电容)

  1. 充电A
  2. 转移A->B
  3. 充电A
  4. 转移A->B 此时A中当前电量为2,操作完成,所以输出4。 对于(2,3,4),显然不可能完成,输出0。

若 c &gt; a c &gt; a c>a且 c &gt; b c &gt; b c>b,则显然不可能完成,M为0。 若 c = a c = a c=a或 c = b c = b c=b,则显然一次操作可完成,M为1。 现在考虑剩余情况,即 c &lt; M a x ( a , b ) c &lt; Max(a, b) c<Max(a,b)且 c ≠ M i n ( a , b ) c \neq Min(a, b) c̸​=Min(a,b)(条件1)

这里我们将电容A、B看作一个整体,整体电容只有充电和放电两种操作。其中充电使整体总电量增加,放电使整体总电量减少。 则系统总电量可以达到的数值为 a x + b y ax + by ax+by(x、y为整数),其中 x x x、 y y y中为正的表示充电,为负的表示放电。因为条件1,所以 x x x、 y y y一定是一正一负,即 x y &lt; 0 xy &lt; 0 xy<0。 要使电量为c,则要满足等式 a x + b y = c ax + by = c ax+by=c。根据 裴蜀定理 ,等式可以满足当且仅当 g c d ( a , b ) ∣ c gcd(a,b)|c gcd(a,b)∣c,即a,b的最大公约数也是c的因子。 相反,若 c m o d &ThinSpace;&ThinSpace; g c d ( a , b ) ≠ 0 c \mod gcd(a, b) \neq 0 cmodgcd(a,b)̸​=0,则电量不能达到c,M为0。

现在假设 g c d ( a , b ) ∣ c gcd(a,b)|c gcd(a,b)∣c,即存在一正一负的整数 x x x, y y y满足等式 a x + b y = c ax + by = c ax+by=c,,则整体电容的具体操作流程如下:

  1. 给电容A充电
  2. 电容A转移到电容B
  3. 若电容B满电,则其放空电量
  4. 如果电容A电量非空,则转2
  5. 转1重复执行整个过程,直到某时刻电容A或B中电量为c为止

其电容操作次数有如下两种情况: (1)若 c &gt; a c &gt; a c>a(此时 a &lt; c &lt; b a &lt; c &lt; b a<c<b),则最终是B中先有c的电量(A存不下),此时有 x x x次充电, − y -y −y次放电, x − y x-y x−y次转移(因为B中先有c的电量,每次A充电必然要转移到B,每次B放电必然A非空由步骤4转步骤2进行转移操作),共 2 ( x − y ) 2(x-y) 2(x−y)次操作。 (2)若 c &lt; a c &lt; a c<a(此时 c &lt; a 且 c &lt; b c &lt; a 且 c &lt; b c<a且c<b),则最终是A中先有c的电量(若B中先有,因为 a 、 b a、b a、b都大于 c c c,所以必然是A转移c电量给空的B,此时A先有c电量,矛盾),此时有 x x x次充电, − y − 1 -y - 1 −y−1次放电(当A中有c的电量时,必然是转移给B后,A中剩余c的电量,此时B中满电量不用放电也满足题目要求,所以公式计算的放电次数需要减1), x − y − 1 x - y - 1 x−y−1次转移(即实际需要充电次数 x x x加上实际需要放电次数 − y − 1 -y - 1 −y−1),共 2 ( x − y − 1 ) 2(x - y - 1) 2(x−y−1)次操作。

因此只需要最小化 ∣ x ∣ + ∣ y ∣ |x| + |y| ∣x∣+∣y∣。若使用扩展欧几里德算法求出任意一组解 ( x , y ) (x, y) (x,y),则等式 a x + b y = c ax + by = c ax+by=c的全部整数解为: { x + k b / g c d ( a , b ) y − k a / g c d ( a , b ) , k = . . . , − 2 , − 1 , 0 , 1 , 2 , . . . \left\{\begin{matrix} x + kb / gcd(a, b) \\ y - ka / gcd(a, b) \end{matrix} , k = ..., -2, -1, 0, 1, 2, ... \right. { x+kb/gcd(a,b)y−ka/gcd(a,b)​,k=...,−2,−1,0,1,2,... 找出其中距离 0 0 0最近的两组解 ( x , y ) (x, y) (x,y)(一组 x &gt; 0 x &gt; 0 x>0,另一组 y &gt; 0 y &gt; 0 y>0),其中 ∣ x ∣ + ∣ y ∣ |x| + |y| ∣x∣+∣y∣ 最小的一组就是达到最小操作数的解 ( x , y ) (x, y) (x,y),根据上面分析可得到最小操作数。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

LL extend_gcd(LL a, LL b, LL &x, LL &y)	// 扩展欧几里德算法
{ 
        
	if(0 == b)
	{ 
        
		x = 1;
		y = 0;
		return a;
	}
	
	LL q = extend_gcd(b, a % b, x, y);
	int t = x;
	x = y;
	y = t - a / b * y;

	return q;
}

int main()
{ 
        
	int N;
	cin >> N;
	while(N --) { 
        
		LL a, b, c, x, y;
		cin >> a >> b >> c;
		int d = extend_gcd(a, b, x, y);  // ax + by = d
		if(c > a && c > b || c % d) { 
          // 不可能完成
			cout << 0 << endl;
			continue;
		}
		if(c == a || c == b) { 
          // 1次操作完成
			cout << 1 << endl;
			continue;
		}

		if(y > 0) { 
          // xy < 0,令x > 0(x < 0时将a、b,x、y交换)
			swap(x, y);
			swap(a, b);
		}
		
		// 使ax + by = c (d是c的因子,即对上面式子ax + by = d两边同时乘以c / d)
		x *= c / d;  
		y *= c / d;
		
		LL a2 = a / d, b2 = b / d;
		LL k = x / b2;
		x -= k * b2;  // 使这组(x, y)最接近0(x > 0时的整数解)
		y += k * a2;
		
		LL res;
		if(c > a) // 情况(1)
			res = 2 * (x - y);
		else // 情况(2)
			res = 2 * (x - y - 1);
		
		x -= b2;  // 使这组(x, y)最接近0(y > 0时的整数解)
		y += a2;
		if(c > b) // 情况(1)
			res = min(res, 2 * (y - x));  // 上文分析中假设x > 0,这里y > 0
		else // 情况(2)
			res = min(res, 2 * (y - x - 1));
			
		cout << res << endl;
	}
}


参考:

  1. https://www.acwing.com/blog/content/17/
  2. https://baike.baidu.com/item/裴蜀定理
  3. https://baike.baidu.com/item/扩展欧几里德算法

标签: gcd电容

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台