现在有n件物品集合V,每件有一个重量Wi,现在要从里面选出一个集合S,使得V-S中的物品都能由S中的物品称出来,即在一个天平的的左边放入V-S中的1件物品,那么必定可以从S中选出一些物品(可以无限使用)放在右边,使得天平平衡。
现在要求集合S中元素个数的最小值。
现在,我们先求出Wi(i=1,2,3...n)的最大公倍数g,然后把每个Wi都除以g,这样gcd(Wi)=1,这样并不会影响最后的结果。现在证明如果V中的一个子集S的最大公倍数为1,那么V中的元素都可以有S中的元素称出来。
证明用的是
Bézout's identity定理。
那么现在的问题转换成从集合V中选出最小的集合S,使得集合S的公倍数于集合V的公倍数相同(同为1)。
设DP[x]为以x为最大公倍数的最小集合的个数,通过DP求解(其实就是BFS搜索),那么最后的结果就是DP[1]
int
d[
10000100
];
class
BalanceScale
{
public
:
int
takeWeights(vector
<
int
>
w)
{
int
n
=
w.size();
int
g
=
w[
0
];
for
(
int
i
=
1
; i
<
n; i
++
) g
=
gcd( g, w[i] );
for
(
int
i
=
0
; i
<
n; i
++
) w[i]
/=
g;
sort( w.begin(), w.end() );
if
( w[
0
]
==
1
)
return
1
;
queue
<
int
>
q;
memset( d,
1
, sizeof( d ) );
for
(
int
i
=
0
; i
<
n; i
++
) { q.push( w[i] ); d[w[i]]
=
1
; }
while
(
!
q.empty()
&&
d[
1
]
==
0x01010101
)
{
int
u
=
q.front(); q.pop();
for
(
int
i
=
0
; i
<
n; i
++
)
{
int
v
=
gcd( u, w[i] );
if
( d[v]
==
0x01010101
) { d[v]
=
d[u]
+
1
; q.push( v ); }
}
}
return
d[
1
];
}
};
下面是DFS的代码:
import
java.util.
*
;
public
class
BalanceScale {
int
gcd(
int
x,
int
y) {
while
(y
!=
0
) {
int
t
=
x
%
y; x
=
y; y
=
t;
}
return
x;
}
int
[] w;
int
answer;
void
bt(
int
i,
int
d,
int
c) {
if
(c
>=
answer) {
return
;
}
if
(i
==
w.length) {
if
(d
==
1
) {
answer
=
c;
}
return
;
}
int
d1
=
gcd(d, w[i]);
if
(d1
!=
d) {
bt(i
+
1
, d1, c
+
1
);
}
bt(i
+
1
, d, c);
}
public
int
takeWeights(
int
[] weight) {
int
d
=
0
;
for
(
int
x : weight) {
d
=
gcd(d, x);
}
for
(
int
i
=
0
; i
<
weight.length; i
++
) {
weight[i]
/=
d;
}
w
=
weight;
answer
=
weight.length;
bt(
0
,
0
,
0
);
return
answer;
}
}