1. 程式人生 > >中石油acm4985: Going Dutch 還錢問題(狀壓dp)

中石油acm4985: Going Dutch 還錢問題(狀壓dp)

HP ces nec ins num ransac aid tor line

4985: Going Dutch

時間限制: 1 Sec 內存限制: 128 MB
提交: 102 解決: 11
[提交][狀態][討論版][命題人:admin]

題目描述

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個人之間存在借錢問題,問最少發生幾次還錢行為,使這n個人之間還清。

【分析】

n個人至多可以有n-1次還錢行為即可還清(樹圖),若能把n個人分為兩個集合,每個集合內能還清,則最多需要n-2次還錢行為。

若能把n個人分為k個能各自還清的集合,則需要n-k次還錢行為。故此題最大化k即可。

狀態壓縮。用一個數字代表一個狀態,其二進制位上為1,代表此人選中,0代表不選。

每個數字代表一個集合。

【代碼】

技術分享圖片
#include<bits/stdc++.h>
using namespace std;

int n,m;
int a[30],dp[1<<22],b[1<<22];
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int x,y,z; scanf("%d%d%d",&x,&y,&z);
        a[x]-=z;
        a[y]+=z;
    }
    for(int i=0;i<(1<<n);i++)
    {
        for(int j=0;j<n;j++)if(i&(1<<j))
        {
            b[i]+=a[j];
        }
    }
    for(int i=0;i<(1<<n);i++)
    {
        for(int j=0;j<n;j++)if((i&(1<<j))==0)
        {
            dp[i|(1<<j)]=max(dp[i|(1<<j)],dp[i]+(b[i|(1<<j)]==0));
        }
    }
    cout<<n-dp[(1<<n)-1]<<endl;
}
View Code

中石油acm4985: Going Dutch 還錢問題(狀壓dp)