某连锁店开设了若干门店,门店间允许进行商品借调以应对暂时性的短缺。本月商品借调的情况记于数组 distributions
,其中 distributions[i] = [from,to,num]
,表示从 from
门店调配了 num
件商品给 to
门店。
若要使得每一个门店最终借出和借入的商品数量相同,请问至少还需要进行多少次商品调配。
注意:一次商品调配以三元组 [from, to, num]
表示,并有 from ≠ to
且 num > 0
。
解题思路:首先要了解一个小知识点,对于一个集合 k,如果集合k且除空集以外的任意子集 满足子集中的所有元素的和不等于0,此时将集合 k变为各项是0的最小调配次数为 k的元素个数-1,知道了这个知识点再用简单的动态规划就可以解决了,动归的思路是dp[a]和dp[b]+dp[c]中取最小值,a是b和c的异或(两个集合合并除去重合的部分),java代码如下。
class Solution {
public int minTransfers(int[][] distributions) {
int k = 12;
int[] arr = new int[k];
int[] sum = new int[1 << k];
int[] dp = new int[1 << k];
for (int[] v : distributions) {
int from = v[0], to = v[1], num = v[2];
arr[from] -= num;
arr[to] += num;
}
//位运算来列举所有子集
for (int i = 1; i < (1 << k); i++) {
for (int j = 0; j < k; j++) {
//当两个集合互无交集时
if ((i & (1 << j)) == 0) {
sum[i] += arr[j];
}
}
if (sum[i] == 0) {
//计算二进制中有多少位1
dp[i] = getcount(i) - 1;
}
}
dp[0] = 0;
//动态规划寻找最小值
for (int i = 1; i < (1 << k); i++) {
if (sum[i] == 0) {
for (int j = i; j != 0; j = (j - 1) & i) {
if (sum[j] == 0) {
dp[i] = Math.min(dp[i], dp[j] + dp[i ^ j]);
}
}
}
}
return dp[(1 << k) - 1];
}
public int getcount(int i) {
int sum = 0;
while (i != 0) {
if((i & 1) == 1) {
sum ++;
}
i = i>>1;
}
return sum;
}
}