連結地址:https://acm.uestc.edu.cn/contest/198/problem/J
我們都知道人民幣的面值是1、2、5、10,為什麼是這個數值呢,我們分析了下發現,從的每個數字都可以由每種面值選出至多一張透過加法和減法(找錢)來構成,(比如:1+2=3,5−1=4,5+1=6,5+2=7,1+2+5=8,10−1=9,)
但是實際上,我們只需要1、2、7三種面值就可以組成的每一個數字了
(1+2=3,7−1−2=4,7−2=5,7−1=6,7+1=8,7+2=9,7+1+2=10)
那麼現在問題來了,給一個數,請問最少需要多少種不同的面值就可以構成從的所有數字,注意在構成每一個數字時同種面值不能超過張。
Standard Input
一個數字(1<=<=100000)、
Standard Output
一個數字,代表最少需要多少種不同的面值可以構成從的所有數字。
Samples
Input | Output |
---|---|
|
|
Time Limit | 1000 ms |
Memory Limit | 64 MiB |
Output Limit | 64 MiB |
題解與思路:
方法一思路:將該題的實質轉化為三進位制問題,如果加面額則為+1,減去面額則為-1,不用該面額的紙幣則為0;所以n張紙幣則有3n種情況(打個比方:如果有兩張紙幣,則狀態共有0 0,1 0,-1 0,0 1,1 1,-1 1,0 -1,1 -1,-1 -1共9種),首先很明顯能除去n張紙幣狀態全為0的情況,剩餘3n-1種情況,剩餘情況中有一半面額之和為負。舉個例子:目前有兩張紙幣面額分別為1,3,則有32-1=8種情況,即1+0=1 , 3-1=2 , 3+0=3 , 3+1=4 , -1+0=-1 , 1-3=-2 , -3+0=-3 , -1+(-3)=-4,可以看出,正數情況和負數情況是相等的,所以正情況一共有3n-1/2種
疑問:正情況只代表了最大值和最小值的取值範圍,怎麼能夠證明出這個範圍區間內的所有值都能取到?
證明:利用數學歸納法,很明顯,k=1時明顯成立(1元最少只用一張紙幣,紙幣面額為1),k=3時,能表示[1~4]的所有面額,表示的最大數剛好為正情況的最多種數(即3n-1/2種),假設k=n-1時也成立(紙幣面額為1,3,9······3n-2)此時n-1張紙幣能表示的範圍為[1~3n-1-1/2],當k=n時(紙幣面額為1,3······3n-1即新增加的面額為3n-1),新增加的表示範圍為[3n-1+1/2~3n-1/2]=>證明1:因為k=n-1時所能表示的正範圍為[1~3n-1-1/2],由上面的思路可得,負數和正數的情況是對稱的,所以能表示的全範圍應該是[-(3n-1-1/2)~3n-1-1/2],此時加上一個新的數3n-1,則將整個區間向前位移3n-1
得新增加的區間[3n-1+1/2~3n-1/2]。此時將新增加的區間和原區間進行合併可得k=n所能表示的區間為[1~3n-1/2](3n-1/2不是整數!),故得證;
方法二思路:不妨設目前能表示的範圍為[1~M],此時增加一個數Q>M,由上面證明中的證明1可得,新增加的可表示範圍為[Q-M~Q+M],要使得增加一個數Q後的區間所能表示的範圍更大,並且區間連續無中斷,即Q-M=M+1 => Q=2*M+1,所以增加Q後的最大表示範圍為[1~3*M+1],由於我們能知道當人民幣面額為1時所能表示的區間為[]1~1],此時只要區間最大值M不超過n,則可以增加一個數Q,使得區間最大值變為3*M+1,直到n包含在區間內;
方法二的實現程式碼如下:
#include<iostream>
using namespace std;
int main(){
int n,max=1,count=1;
cin>>n;
if(n==1)
cout<<count<<endl;
else
{
while(max<n)
{
max=3*max+1;
count++;
}
cout<<count<<endl;
}
return 0;
}