poj-2142 The Balance 擴展歐幾里德算法

題目鏈接

poj-2142 The Balance

題目

The Balance
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 8015 Accepted: 3506

Description

Ms. Iyo Kiffa-Australis has a balance and only two kinds of weights to measure a dose of medicine. For example, to measure 200mg of aspirin using 300mg weights and 700mg weights, she can put one 700mg weight on the side of the medicine and three 300mg weights on the opposite side (Figure 1). Although she could put four 300mg weights on the medicine side and two 700mg weights on the other (Figure 2), she would not choose this solution because it is less convenient to use more weights. 
You are asked to help her by calculating how many weights are required. 

Input

The input is a sequence of datasets. A dataset is a line containing three positive integers a, b, and d separated by a space. The following relations hold: a != b, a <= 10000, b <= 10000, and d <= 50000. You may assume that it is possible to measure d mg using a combination of a mg and b mg weights. In other words, you need not consider "no solution" cases. 
The end of the input is indicated by a line containing three zeros separated by a space. It is not a dataset.

Output

The output should be composed of lines, each corresponding to an input dataset (a, b, d). An output line should contain two nonnegative integers x and y separated by a space. They should satisfy the following three conditions. 
  • You can measure dmg using x many amg weights and y many bmg weights. 
  • The total number of weights (x + y) is the smallest among those pairs of nonnegative integers satisfying the previous condition. 
  • The total mass of weights (ax + by) is the smallest among those pairs of nonnegative integers satisfying the previous two conditions.

No extra characters (e.g. extra spaces) should appear in the output.

Sample Input

700 300 200
500 200 300
500 200 500
275 110 330
275 110 385
648 375 4002
3 1 10000
0 0 0

Sample Output

1 3
1 1
1 0
0 3
1 1
49 74
3333 1

Source

題意

題意是給出3個值:a、b、c,a與b表示2種不同的砝碼的重量,c表示一個物品的重量。物品的重量雖然給出,但是題目中是不知道的,但是砝碼重量明確標出。現在將物品與砝碼按照一定的規模放置,求分別需要多少件a、b物品才能夠準確稱出物品的具體重量。要求砝碼的數量最小,如果有多種方法使得數量達到最小,則選出使用砝碼總質量最少的那一種方法。保證數據一定有唯一解。

題解

首先,標準列方程問題,因爲只有2種砝碼,所以一定是兩個變量。如果是要求平衡,那麼可以得到等式:

            ax+by = c

x與y可以爲負數。可以理解爲,正數表示和物品在同一邊,負數表示不在同一邊。而x、y就代表了a、b的數量。所以一定是0或者正整數。那麼這個問題也就可以化成求x、y的最小解的問題。

同時題目中描述,要求砝碼總質量最小,同時要求在總質量相同的時候總數量最小。那麼整個問題其實就是求|x|+|y|的最小值。在存在多解的情況下求a|x|+b|y|的最小值。

無論怎樣,這個題仍然是求解集的,至少要先利用擴展歐幾里德算法將通解求出。這裏首先要明白一點,擴展歐幾里德算法計算的實際上是:ax+by = gcd(a,b),也就是說我們首先應該進行一些轉換。根據一條性質:

            a*(c/gcd(a,b))*x + b*(c/gcd(a,b))*y = c/gcd(a,b)

我們可以對x、y進行操作,從而令其由ax+by = gcd(a,b)的解集變成ax+by = c的解。即x = x*(c/gcd(a,b)),y = y*(c/gcd(a,b))。之後,我們就可以正常計算通解:x = x0 + b/gcd(a,b)*k,y = y0 - a/gcd(a,b)*k。

之後我們需要分析a|x|+b|y|的最小值問題。如果我們能夠令a > b恆存在,那麼y的變化率一定是比x的變化率大的。這樣,可以確定k在0 = y0 - a/gcd(a,b)*k的解的範圍內可以取得正確答案。

那麼我們可以對k的一定範圍內的值進行枚舉,看那個k值代入可以令x、y得到一個最小值。

C++ AC代碼0ms

#include<iostream>
#include<vector>
#include<string>
#include<math.h>
#include<algorithm>
#include<map>
#include<utility>
#include<queue>
#include<memory>
#include<stack>
#include<sstream>
#define TIME std::ios::sync_with_stdio(false)
#define LL long long
#define MAX 5
#define INF 0x3f3f3f3f

using namespace std;

int num[1010];

int exgcd(int a,int b,int &x,int &y){
    if(b == 0){
        x = 1;
        y = 0;
        return a;
    }
    int ans = exgcd(b,a%b,x,y);
    int temp = y;
    y = x - (a/b)*y;
    x = temp;
    return ans;
}

int gcd(int a, int b){
    if(b == 0) return a;
    return gcd(b, a%b);
}

int main(){
    TIME;
    int a,b,d;
    while(cin >> a >> b >> d){
        if(!a && !b && !d) break;
        int x,y;
        int m = gcd(a,b);
        if(d % m != 0){  // 判定是否有解,不過看別人的代碼,好像數據中沒有這種情況
            cout << "no solution" << endl;
            continue;
        }
        int flag = 0;
        if(a < b){  // 確保a > b
            flag = 1;
            swap(a,b);
        }
        exgcd(a,b,x,y);
        x = x*d/m;
        y = y*d/m;  // 對解進行轉化,擴展歐幾里德算法是默認解的翡蜀方程
        int t = (y*m)/a;  // 求出k的值(代碼中是t)
        int ans = INF;
        int x1,x2,y1,y2;
        for(int i = t-5;i <= t+5;i++){  // 枚舉範圍5即可
            x1 = x + (b*i)/m;
            y1 = y - (a*i)/m;
            if(abs(x1) + abs(y1) < ans){
                x2 = x1;
                y2 = y1;
                ans = abs(x1) + abs(y1);
            }
        }
        if(flag == 0){
            cout << abs(x2) << " " << abs(y2) << endl;  // 因爲a、b調換了位置,所以輸出順序可能不一樣
        }else{
            cout << abs(y2) << " " << abs(x2) << endl;
        }
    }

    system("pause");
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章