Leetcode 465. 最优账单平衡

1.题目基本信息

1.1.题目描述

给你一个表示交易的数组 transactions ,其中 transactions[i] = [fromi, toi, amounti] 表示 ID = fromi 的人给 ID = toi 的人共计 amounti $ 。

请你计算并返回还清所有债务的最小交易笔数。

1.2.题目地址

https://leetcode.cn/problems/optimal-account-balancing/description/

2.解题方法

2.1.解题思路

子集状态压缩动态规划(子集状压dp),使用二进制整数来表示数组cnts中的子集的动态规划

2.2.解题步骤

第一步,构建各个交易的cnts数组,以及进行状态定义。

  • 1.1.构建cnts数组,cnts总共12个位置(因为交易总数最多12个),cnts[i]表示交易人的待进账(也就是借出去的总额)

  • 1.2.状态定义。dp[i]表示子集i的最小交易次数,这里使用二进制数字i中各个位上为1的状态确定cnts中的子集(也就是状态压缩),对于长度为n的数组,也刚好有2^n个子集。

第二步,状态初始化。dp[0]=0,表示空子集的转换次数为0;这在初始化时已经完成。

第三步,状态转移。如果i子集的和不为0,则不可能转换完成,那么dp[i]=inf;如果i子集的和为0,则枚举i的子集j,dp[i]=min([dp[j]+dp[i^j] for j in i的子集])(其中i^j为i中j子集的补集)(i的子集通过k=(i-1)&i,k=(k-1)&i进行枚举)

  • 3.1.计算子集i的和

  • 3.2.子集和不为0,则不可能通过有限的交易使其全部为0,此时dp[i]=inf

  • 3.3.子集和为0,则枚举i子集的子集j,进行状态转移,每次j更新都获取距离当前子集最近的i的子集(这是枚举子集的模板方法,记下来)

第四步,m-1子集即所有12个人都参与进行交换,哪些不存在的人的cnts为0。所以dp[m-1]即为题解

3.解题代码

python代码

class Solution:
    def minTransfers(self, transactions: List[List[int]]) -> int:
        # 思路:子集状态压缩动态规划(子集状压dp),使用二进制整数来表示数组cnts中的子集的动态规划
        # 第一步,构建各个交易的cnts数组,以及进行状态定义。
        # 1.1.构建cnts数组,cnts总共12个位置(因为交易总数最多12个),cnts[i]表示交易人的待进账(也就是借出去的总额)
        n = 12
        m = 1 << n
        cnts = [0] * n
        for f, t, amount in transactions:
            cnts[f] += amount
            cnts[t] -= amount
        # 1.2.状态定义。dp[i]表示子集i的最小交易次数,这里使用二进制数字i中各个位上为1的状态确定cnts中的子集(也就是状态压缩),对于长度为n的数组,也刚好有2^n个子集。
        dp = [0] * m
        # 第二步,状态初始化。dp[0]=0,表示空子集的转换次数为0;这在初始化时已经完成。
        # 第三步,状态转移。如果i子集的和不为0,则不可能转换完成,那么dp[i]=inf;如果i子集的和为0,则枚举i的子集j,dp[i]=min([dp[j]+dp[i^j] for j in i的子集])(其中i^j为i中j子集的补集)(i的子集通过k=(i-1)&i,k=(k-1)&i进行枚举)
        for i in range(m):
            # 3.1.计算子集i的和
            sum_ = 0
            for j in range(n):
                if (i >> j) & 1:    # i子集的二进制第i位上是1
                    sum_ += cnts[j]
            if sum_ != 0:
                # 3.2.子集和不为0,则不可能通过有限的交易使其全部为0,此时dp[i]=inf
                dp[i] = inf
            else:
                # 3.3.子集和为0,则枚举i子集的子集j,进行状态转移,每次j更新都获取距离当前子集最近的i的子集(这是枚举子集的模板方法,记下来)
                dp[i] = bin(i).count('1') - 1
                j = (i - 1) & i
                while j > 0:
                    dp[i] = min(dp[i], dp[j] + dp[i ^ j])
                    j = (j - 1) & i
        # print(dp)
        # 第三步,m-1子集即所有12个人都参与进行交换,哪些不存在的人的cnts为0。所以dp[m-1]即为题解
        return dp[m - 1]

4.执行结果

posted @ 2025-05-31 08:54  Geek0070  阅读(26)  评论(0)    收藏  举报