标签:
//题目描述:给定一个n*m矩阵,从矩阵中选取k = min(n,m)个数,使这k个数互相都不同行同列。问如何选择使k个数的和最大。 // 可以通过最小费用最大流的思想来求解该问题,建一个超级起点s,一个超级汇点t。 // 把矩阵的第一维标记节点1~n,第二维节点标记 n+1~m+n // 加边操作: 把起点 与1~n的节点都连一条边,边的流量为1,花费是0,把n+1~n+m的节点都与汇点连一条边,边的流量是1,花费是0; // 对于矩阵中的值a[i][j]. 在i节点与i+j节点连一条边,流量是1,花费是-a[i][j]。(花费取负就是为了求最大值,负的越大,取反就越大) // 通过建图完成,就可以使用最小费用最大流模型了 #include <stdio.h> #include <string.h> #include <algorithm> using namespace std ; typedef long long LL ; #define clr( a , x ) memset ( a , x , sizeof a ) const int MAXN = 105 ; //节点数 const int MAXE = 1000000 ; //边数 const int INF = 0x3f3f3f3f ; struct Edge { int v , c , w , n ; //v 邻接节点,c表示流量,w表示花费,n表示u(与v相连的节点)的另一个节点所在边的值。 Edge () {} Edge ( int v , int c , int w , int n ) : v ( v ) , c ( c ) , w ( w ) , n ( n ) {} } ; Edge E[MAXE] ; //边的集合 int H[MAXN] , cntE ; //cntE表示边的数量 H表示头节点 int d[MAXN] , cur[MAXN] , vis[MAXN] ; //d表示节点的最短路径 int Q[MAXN] , head , tail ; //模拟队列 int flow , cost ; //流量和花费 int s , t ; //超级起点,超级汇点 int n , m ; //矩阵大小 int G[MAXN][MAXN] ; //保存输入数据 void init () { cntE = 0 ; clr ( H , -1 ) ; } //加边操作,反向边也需要加入 void addedge ( int u , int v , int c , int w ) { E[cntE] = Edge ( v , c , +w , H[u] ) ; H[u] = cntE ++ ; E[cntE] = Edge ( u , 0 , -w , H[v] ) ; H[v] = cntE ++ ; } //通过spfa寻找最短路,并进行更新 int spfa () { head = tail = 0 ; clr ( vis , 0 ) ; //visit 表示是否加入队列中 clr ( d , INF ) ; Q[tail ++] = s ; cur[s] = -1 ; //标记父节点 d[s] = 0 ; //最小花费代价 while ( head != tail ) { int u = Q[head ++] ; if ( head == MAXN ) head = 0 ; vis[u] = 0 ; for ( int i = H[u] ; ~i ; i = E[i].n ) { int v = E[i].v ; if ( E[i].c && d[v] > d[u] + E[i].w ) { d[v] = d[u] + E[i].w ; cur[v] = i ; if ( !vis[v] ) { Q[tail ++] = v ; if ( tail == MAXN ) tail = 0 ; vis[v] = 1 ; } } } } if ( d[t] == INF ) return 0 ; cost += d[t] ; flow ++ ; for ( int i = cur[t] ; ~i ; i = cur[E[i ^ 1].v] ) { E[i].c -- ; E[i ^ 1].c ++ ; } return 1 ; } //重复寻找最短路,更新流量 int mcmf () { flow = cost = 0 ; while ( spfa () ) ; return cost ; } void solve () { n = 7 ; m = 7 ; // scanf ( "%d%d" , &n , &m ) ; init () ; s = 0 , t = n + m + 1 ; //超级起点加边 for ( int i = 1 ; i <= n ; ++ i ) { addedge ( s , i , 1 , 0 ) ; } //超级汇点加边 for ( int i = 1 ; i <= m ; ++ i ) { addedge ( i + n , t , 1 , 0 ) ; } //矩阵中的值加边 for ( int i = 1 ; i <= n ; ++ i ) { for ( int j = 1 ; j <= m ; ++ j ) { scanf ( "%d" , &G[i][j] ) ; addedge ( i , j + n , 1 , -G[i][j] ) ; } } mcmf () ; //最终最小费用取反就是最大值 printf ( "%d\n" , -cost ) ; } int main () { solve () ; return 0 ; }
标签:
原文地址:http://www.cnblogs.com/ChenAlong/p/4683098.html