题解 | #M Water#

Water

https://ac.nowcoder.com/acm/contest/57355/M

Problem: M题 Water

题意

  • 给两个容量分别为ABA,B的杯子
  • 给以下四种操作:
    • AABB注满水
    • AABB中水倒掉
    • 喝光AABB杯子中的水
    • AA杯子中水倒进BB或将BB杯子中水倒进AA
  • 求每次用上述四种方法的一种,最少几次可以喝xx水(如果不能输出1-1

思路

题目就是任意A,A,+B,BA, -A, +B, -B的和等于xx,即rA+sB=xrA + sB = x,对于这个式子,由裴蜀定理有如果x%gcd(A,B)!=0x \% gcd(A, B) != 0,此题无解。当此题有解时,而对于式子rA+sB=xrA + sB = x求解rsr、s可用拓展欧几里得算法(exgcd)(exgcd)
发现当求解出r>=0,s>=0r>=0,s >= 0,我们操作一定是装AAAA,装BBBB,直到满足喝rA,sBr * A, s * B,这样我们的操作数就是2(r+s)2 * (r + s)
如果rs<0r * s < 0,不妨令r>0,s<0r > 0, s < 0。则我们操作是,先装AA,将AA倒入BB中,如果BB满了,就把BB倒掉,直到将装满过ssBB,同时最后一个BB可以不倒,这样我们的操作数就是2(r+abs(s))12 * (r + abs(s)) - 1

解题方法

我们先用exgcdexgcd求出rsr、s,因为我们算最小操作数的式子2(r+s)2 * (r + s)2(r+abs(s))12 * (r + abs(s)) - 1,我们需要在rr接近00或者ss接近00时,可以得到最优解。(rr就是操作AA的次数,ss就是操作BB的次数,我们自己玩一玩是不是也可以感觉到越接近于只用一种杯子,就可以用更少的次数)

复杂度

  • 时间复杂度:

每次exgcdexgcd得到rsr,s,然后只在00点附近跑很小的范围,因此每次查询复杂度是O(logn)O(logn),总复杂度就是O(nlogn)O(nlogn)

  • 空间复杂度:

没有数组,复杂度是常量级的O(1)O(1)

Code

#include <bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

#define endl "\n"
#define ll long long

//板子
ll exgcd(ll a, ll b, ll &x, ll &y){
	if(!b){
		x = 1, y = 0;
		return a;
	}

	ll d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	
	return d;
}

void solve()
{
	ll a, b, x;
	cin >> a >> b >> x;

	ll r, s;
	ll d = exgcd(a, b, r, s);

	if(x % d) return cout << -1 << endl, void();

	a /= d, b /= d, x /= d, r *= x, s *= x;
    
	ll ans = 1e18;
	
    //在附近取值
	auto getans = [&](ll t){
		ll n = r + t * b, m = s - t * a;
		if(n >= 0 && m >= 0) ans = min(ans, 2 * (n + m));
		else ans = min(ans, 2 * abs(n - m) - 1);
	};

	ll t0 = -r / b;
	for(int i = t0 - 1; i <= t0 + 1; i++) getans(i);
	t0 = s / a;
	for(int i = t0 - 1; i <= t0 + 1; i++) getans(i);

	cout << ans << endl;
}

int main()
{
	cout.tie(nullptr);cin.tie(nullptr);ios::sync_with_stdio(false);

	int _ = 1;
	cin >> _;
	while (_--) solve();

	return 0;
}

PS:欢迎大家批评指正

全部评论

相关推荐

3 1 评论
分享
牛客网
牛客企业服务