BAPC Going Dutch

You and your friends have just returned from a beautiful vacation in the mountains of the Netherlands. When on vacation, it’s annoying to split the bill on every expense every time, so you just kept all the receipts from the vacation, and wrote down who paid how much for who. Now, it is time to settle the bill.
You could each take all the receipts showing that someone paid something for you, and then pay that person back. But then you would need a lot of transactions, and you want to keep up the lazy spirit from your trip. In the end, it does not matter who transfers money to whom; as long as in the end, everyone’s balance is 0.
Can you figure out the least number of transactions needed to settle the score? Assume everyone has enough spare cash to transfer an arbitrary amount of money to another person.

输入

Input consists of
• A line containing two integers M, the number of people in the group, with 1 ≤ M ≤ 20,and N, the number of receipts from the trip, with 0 ≤ N ≤ 1000.
• N lines, each with three integers a, b, p, where 0 ≤ a, b < M, and 1 ≤ p ≤ 1000,signifying a receipt showing that person a paid p euros for person b.

输出

Output a single line containing a single integer, the least number of transactions necessary to settle all bills.

样例输入

4 2
0 1 1
2 3 1

样例输出

2

【题意】
n个人m笔帐,问你最少还多少次能还清。

【分析】

对于k个人来说,如果这k个人的集合正负钱数和为0(即这k个人相互还钱能还清),显然是最多还(k-1)次账的。(因为最差的情况是k-1个人都还给最后一个人)
所以就显然可以得到的是,如果我们将这n个人分波,分成k和(n-k)两个部分显然可以得到分别的还钱次数是(k-1)和(n-k-1),即总还钱次数为(k-1)+(n-k-1) = (n-2)。同理可得,如果将n个人分成x波,使得这x波分别都能还清,那么显然可以得到,只需要还(n-x)的。所以我们只需要统计最大的x,使得这x波人能还清,那么就可以轻松的得到答案了。

【代码】

#include <bits/stdc++.h>
using namespace std;
int n,m;
const int maxm = 1<<20;
int num[20];
int ans1[maxm];
int ans[maxm];
int main() {
    //freopen("C:\\Users\\acm2017\\Desktop\\in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    int a,b,c;
    memset(num,0,sizeof(num));
    while(m--)
    {
        scanf("%d%d%d",&a,&b,&c);
        num[a]+=c;
        num[b]-=c;
    }
    for(int i = 0;i<(1<<n);i++)
    {///预处理出每个集合的总和
        ans1[i] = 0;
        for(int j = 0;j<n;j++)
            if(i&(1<<j))
                ans1[i]+=num[j];
    }
    for(int i = 0;i<(1<<n);i++)
    {///枚举每一个集合
        for(int j = 0;j<n;j++)
            if((i&(1<<j))==0)///当前这个集合里面没有第j个人
                ans[i|(1<<j)] = max(ans[i|(1<<j)],ans[i]+(ans1[i|(1<<j)]==0?1:0));///如果集合里加上这第j个人可以构成还清的状态,那么显然可以用这个更优解更新答案
    }
    printf("%d\n",n-ans[(1<<n)-1]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zuhiul

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值