原题:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3764
题意:给定一个N个点M条边的带权图,你可以删掉不超过K条边,求删边后图的最大流最小是多少 (N <= 10, M <= 1000, K <= M)
解题思路:
平时我们经常是用最大流求最小割,这次的思路反过来,降低最小割来减少最大流
考虑一个图的最小割,他将一个图划分成两个集合,源和汇各属一个集合,最小割割掉了连接这两个集合的边
那么由于我们现在可以预先删除K条边,所以我们可以将连接两个集合的边中,前K大的先删掉,剩下的加起来就是还需要割的
也就是当前情况下的最大流
最终的解法就是枚举集合的分法,然后搞一搞就行了...
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define LL long long
#define eps 1e-8
#define mxn 15
#define mxe 5005
const LL INF = 2100000000000000LL;
struct edge {
int u, v, c;
bool operator < (const edge& b) const {
return c > b.c;
}
}E[mxe];
LL solve( int msk, int n, int m, int k ) {
if( (msk & 1) == ((msk >> (n - 1)) & 1) )
return INF;
LL ret = 0;
for( int i = 0; i < m; ++i ) {
int u = E[i].u, v = E[i].v, c = E[i].c;
if( ((msk >> (u - 1)) & 1) ^ (((msk >> (v - 1)) & 1)) ) {
if( k )
--k;
else ret += c;
}
}
return ret;
}
int main()
{
int n, m, k;
while( scanf( "%d%d%d", &n, &m, &k ) == 3 ) {
for( int i = 0; i < m; ++i )
scanf( "%d%d%d", &E[i].u, &E[i].v, &E[i].c );
sort(E, E + m);
LL ans = INF;
for( int i = 0; i < (1 << n); ++i )
ans = min(ans, solve(i, n, m, k));
cout << ans << endl;
}
return 0;
}