leetcode 465. 最优账单平衡


题目

一群朋友在度假期间会相互借钱。比如说,小爱同学支付了小新同学的午餐共计 10 美元。如果小明同学支付了小爱同学的出租车钱共计 5 美元。我们可以用一个三元组 (x, y, z) 表示一次交易,表示 x 借给 y 共计 z 美元。用 0, 1, 2 表示小爱同学、小新同学和小明同学(0, 1, 2 为人的标号),上述交易可以表示为 [[0, 1, 10], [2, 0, 5]]。

给定一群人之间的交易信息列表,计算能够还清所有债务的最小次数。

注意:

一次交易会以三元组 (x, y, z) 表示,并有 x ≠ y 且 z > 0。
人的标号可能不是按顺序的,例如标号可能为 0, 1, 2 也可能为 0, 2, 6。


提示:
1 <= transactions.length <= 8
transactions[i].length == 3
0 <= from_i, to_i < 12
from_i != to_i
1 <= amount[i]<= 100

一、dfs

这个感觉还能想想。。。

class Solution 
{
public:
    int res = INT_MAX;
     //如果把moneys[start]与moneys[x]消除,最理想效果是 moneys[start]+moneys[x]=0 这样消除 1 次就可以消除 2 个数字
    //否则需要 消除 2 次 才会把 moneys[start] 和 moneys[x] 都消除为0
    void dfs(vector<int>&money,int start,int count,int& res)
    {
        //如果res < count,说明与i位置数字的消除运算与之前计算过的位置相比已经不是最佳选择,后续计算无意义
        if(res<count)return;
        while(start<money.size()&&money[start]==0)start++;
        //经过N此运算,i位置的数字有可能已经被消为0
        if(start==money.size())
        {
            res=min(res,count);
        }
         //求money[start] 分别与 money[i]、....消除的最小次数的那个
        for(int i=start+1;i<money.size();i++)
        {
            //只有符号不相同的才可以消除
            if(money[i]*money[start]<0)
            {
                //与money[i]的消除,更新money[i]
                money[i]+=money[start];
                // money[start]实际上已经为"0",相同逻辑计算后续的数字
                dfs(money,start+1,count+1,res);
                money[i]-=money[start];
            }
        }
    }
    int minTransfers(vector<vector<int>>& transactions) 
    {
        unordered_map<int,int>umap;
        for(auto v:transactions)
        {
            umap[v[0]]+=v[2];
            umap[v[1]]-=v[2];
        }
        //收集仍然存在债务关系的钱数,-10,-5,5,10
        vector<int> money;
        for(auto x:umap)
        {
            if(x.second!=0)
            {
                money.push_back(x.second);
            }
        }
        dfs(money,0,0,res);
        return res;
    }
};

二、状压dp

其实不咋会状压dp,题解参考:状压dp

class Solution 
{
public:
    int res = INT_MAX;
    int minTransfers(vector<vector<int>>& transactions) 
    {
        unordered_map<int,int>umap;
        for(auto v:transactions)
        {
            umap[v[0]]+=v[2];
            umap[v[1]]-=v[2];
        }
        //收集仍然存在债务关系的钱数,-10,-5,5,10
        vector<int> v;
        for(auto x:umap)
        {
            if(x.second!=0)
            {
                v.push_back(x.second);
            }
        }
 int n = v.size();
        vector<int> sum(1 << n);
        for (int i = 1; i < (1 << n); ++i)
            for (int j = 0; j < n; ++j)
                if (i & (1 << j)) 
                {
                    sum[i] = sum[i ^ (1 << j)] + v[j];
                    break;
                }
        vector<int> dp(1 << n);
        for (int i = 1; i < (1 << n); ++i) 
        {
            if (sum[i] != 0)
                continue;
            dp[i] = 1;
            for (int si = (i - 1) & i; si; si = (si - 1) & i)
                if (dp[si])
                    dp[i] = max(dp[i], dp[si] + 1);
        }
        return n - dp[(1 << n) - 1];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值