为了方便说明,将容量为12品脱,8品脱,5品脱瓶子分别称为大瓶子,中瓶子,小瓶子。按照下面2种规则中的如何一种可以解决这个问题:
以下是求最短即最优解的方法:
规则:
-
大瓶子只能倒入中瓶子
-
中瓶子只能倒入小瓶子
-
小瓶子只能倒入大瓶子
-
小瓶子只有在已经装满的情况下才能倒入大瓶子
-
若小瓶子被倒空,则无论中瓶子是否满,应马上从中瓶子倒入小瓶子
之所以要规定倒酒的顺序是为了防止状态重复。而根据这5条规则,大瓶子每次倒入中瓶子的酒总是8品脱,小瓶子每次倒入大瓶子的酒总是5品脱。(请结合下面的表来理解这句话,理解这点很重要)
有了上面的规定后,倒酒的顺序就确定下来了:
设大,中,小三个瓶子容量分别是C1,C2,C3,需要倒出的容量是R
则实际上要是我们能将容量为R的酒倒到中瓶子和小瓶子中就可以啦(有点废话)
设大瓶子倒满中瓶子X次,从小瓶子中倒入大瓶子Y次。
那么显然由大瓶子累次倒入中瓶子和小瓶子总共C2X的酒。而由小瓶子倒入大瓶子一共有C3Y的酒。
那么最终,小瓶子和中瓶子剩余的酒显然就是 C2X - C3Y
因此,泊松分酒问题实质上转化为下面的不定方程是否有正整数解的问题:
C2X - C3Y = R
对于我们的问题,
C1=12,C2=8,C3=5,R=6
倒酒规则实质上相当于解下面这个不定方程:
8X - 5Y = 6 ( 限定 X > 0 ,Y > 0 )
最小整数解是 X=2,Y= 2
表示倒满8品脱的瓶子2次,5品脱的瓶子倒空2次
那么8品脱的瓶子和5品脱的瓶子剩酒总量必然是 8 * 2 – 5 * 2 = 6
#include<bits/stdc++.h>
using namespace std;
#define A 10
#define B 7
#define C 3
void A_to_B();
void B_to_C();
void C_to_A();
int a=A;
int b=0;
int c=0;
int main(){
while(a!=A/2){
if(b==0)
A_to_B();
if(c==C)
C_to_A();
else if(b!=0)
B_to_C();
}
return 0;
}
void A_to_B(){
b=a>B?B:a;
a-=b;
printf("\na->b:\t a=%d\tb=%d\tc=%d\t",a,b,c);
}
void B_to_C(){
int n=c;
c=b+c>C?C:b+c;
b-=c-n;
printf("\nb->c:\t a=%d\tb=%d\tc=%d\t",a,b,c);
}
void C_to_A(){
a+=c;
c=0;
printf("\nc->a:\t a=%d\tb=%d\tc=%d\t",a,b,c);
}
如果是10次的话,因为我们求的是a->b 和c->a的值,而b->c的值即为其和再减去1,那么10的最优解就是9了
还有一些方法,是求能否分成功,但不一定是最优解:
法一:
根据扩展欧几里得定理:
aX+bY=gcd(a,b)
即只要d%gcd(a,b)==0,那么就算成功
法二:
小桶是a,中桶是b,大桶是c,目标是d。
不断扩大a的倍数
a*n%b==d,就算成功