How to earn more-最小割/FordFulkerson/Dinic

How to earn more
Source:HOJ-2634

Xiao Ming is an expert in computer science and technology, so he can get a lot of projects every month. The projects always bring him a lot of money, now he is thinking how to earn money as more as possible.
Every month he can get m projects, and each project Ai will bring him Xi yuan. Although Xiao Ming is an expert, he still needs to hire some other guys to help him. Of course, the employees are not as good as Xiao Ming, for they are just good at some single aspect. So, they should work together to finish one project. There is a list shows the salary of m employees, who are labeled from 0 to m-1. Xiao Ming only hires employees, in that list, and he knows who will be needed by each project.If one employee is hired, he can join in several projects.

Input
The first line is an integer c shows the number of cases. For each case, the first line has two numbers m,n(m,n <=100), denoting that there is m projects and n employees on the list.The second line has m integers, which are seperated by a single blank, the ith number Ximeans the project Ai will bring Xiao Ming Xi yuan. Xi is less the 10000. The third line has n integers, which are seperated by a single blank, the ith number Yimeans the employee Bi will cost Xiao Ming Yi yuan. And the next m lines will show which part of the employees will be needed by each project. Line i is a list of the employees, who are needed by project Ai. In each line, first a number Zi shows the number of employees needed by this project. And Zi labels of the emloyees follows, which are still seperated by a sigle blank.

Output
You should output a single integer shows the maximun money Xiao Ming can earn in a single month. The money he can earn is equall to the money he can totally get minus the money he totally cost. You should not leave any extra blanks at the end of each line.

Sample Input
1
3 5
30 40 43
55 17 23 22 11
3 0 1 2
3 1 2 3
2 2 1

Sample Output
21

源代码一(Ford-Fulkerson):
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

queue <int> Q;
const int MAX = 208;
const int INF = 1<<30;
int  P , E , S , T;
int  map[MAX][MAX] , p[MAX] , e[MAX] , mark[MAX] , pre[MAX];
//map是网络流图,mark是bfs标志数组,pre是更新的上一个结点

int  FordFulkerson( void );
int  bfs( void );
int  update( void );
int  min( int a , int b ){ return a <= b ? a : b; }

int main( ){
        int t , i , j , n , sum;
        
        cin>>t;

        while( t-- ){
                memset( map , 0 , sizeof( map ) );
                cin>>P>>E;
                sum = 0;
                S = 0;
                T = P + E + 1;
                for( i=1 ; i<=P ; i++ ){
                        cin>>p[i];
                        sum += p[i];    //总的费用叠加
                }
                for( i=1 ; i<=E ; i++ )
                        cin>>e[i];
                for( i=1 ; i<=P ; i++ ){
                        cin>>n;
                        while( n-- ){
                                cin>>j;
                                map[j+1][i+E] = INF;    //形成employee->project流
                        }
                }
                for( i=1 ; i<=E ; i++ )
                        map[S][i] = e[i];    //形成源点->employee流
                for( i=1 ; i<=P ; i++ )
                        map[i+E][T] = p[i];    //形成project->汇点图

                cout<<sum-FordFulkerson()<<endl;        //最小费用与最大流的关系
        }

        return 0;
}

int  FordFulkerson( void ){
        int sum = 0;

        while( bfs( ) ){
                sum += update( );
        }

        return sum;
}

int  bfs( void ){
        int u , v;

        while( !Q.empty() )    //一定要清空
                Q.pop();

        memset( mark , 0 , sizeof( mark ) );
        memset( pre , -1 , sizeof( pre ) );
        Q.push( S );
        mark[S] = 1;

        while( !Q.empty() ){
                u = Q.front();
                Q.pop();

                if( u==T )
                        return 1;

                for( v=S ; v<=T ; v++ ){
                        if( !mark[v] && map[u][v] ){
                                mark[v] = 1;
                                Q.push( v );
                                pre[v] = u;    //前驱结点
                        }
                }
        }

        return 0;
}

int  update( void ){
        int v , u , minimum;

        v = T;
        minimum = INF;

        while( v!=S ){
                u = pre[v];
                minimum = min( minimum , map[u][v] );
                v = u;
        }

        v = T;
        while( v!=S ){
                u = pre[v];
                map[u][v] -= minimum;    //正减
                map[v][u] += minimum;    //逆加
                v = u;
        }

        return minimum;
}


源代码二(Dinic):
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

queue <int> Q;
const int MAX = 208;
const int INF = 1<<30;
int  P , E , S , T;
int  map[MAX][MAX] , p[MAX] , e[MAX] , mark[MAX] , level[MAX];
//level是bfs的层次数组

int  Dinic( void );
int  bfs( void );
int  dfs( int u , int flow );
int  min( int a , int b ){ return a <= b ? a : b; }

int main( ){
        int t , i , j , n , sum;
        
        cin>>t;

        //每个变量表示的含义和上一个代码一致
        while( t-- ){
                memset( map , 0 , sizeof( map ) );
                cin>>P>>E;
                sum = 0;
                S = 0;
                T = P + E + 1;
                for( i=1 ; i<=P ; i++ ){
                        cin>>p[i];
                        sum += p[i];
                }
                for( i=1 ; i<=E ; i++ )
                        cin>>e[i];
                for( i=1 ; i<=P ; i++ ){
                        cin>>n;
                        while( n-- ){
                                cin>>j;
                                map[j+1][i+E] = INF;
                        }
                }
                for( i=1 ; i<=E ; i++ )
                        map[S][i] = e[i];
                for( i=1 ; i<=P ; i++ )
                        map[i+E][T] = p[i];

                cout<<sum-Dinic()<<endl;
        }

        return 0;
}

int  Dinic( void ){
        int sum = 0;

        while( bfs( ) ){    //bfs分层
                sum += dfs( S , INF );    //dfs更新多条增广路
        }

        return sum;
}

int  bfs( void ){
        int u , v;

        while( !Q.empty() )    //一定要有清空队列操作
                Q.pop();

        memset( mark , 0 , sizeof( mark ) );
        memset( level , -1 , sizeof( level ) );
        Q.push( S );
        mark[S] = 1;
        level[S] = 0;

        while( !Q.empty() ){
                u = Q.front();
                Q.pop();

                if( u==T )
                        return 1;

                for( v=S ; v<=T ; v++ ){
                        if( !mark[v] && map[u][v] ){
                                mark[v] = 1;
                                Q.push( v );
                                level[v] = level[u] + 1;    //层次加一
                        }
                }
        }

        return 0;
}

int  dfs( int u , int flow ){
        int v , flowSum , sum=0;

        if( u==T )    //递归到了汇点
                return flow;

        for( v=S ; v<=T ; v++ ){
                if( level[v]==level[u]+1 && map[u][v] ){
                        flowSum = dfs( v , min( map[u][v] , flow ) );    //该结点拓展的所有流出量
                        map[u][v] -= flowSum;    //正减
                        map[v][u] += flowSum;    //逆加
                        flow -= flowSum;        //可流的流向减少
                        sum += flowSum;    //总的流量累加
                }
        }

        return sum;
}

代码分析:之前使用了Dinic算法TLE了,然后就很纳闷,这都能TLE,该不成要学更先进的算法SAP什么的?然后就看了标程才发现bfs队列忘了每次都清空..囧..总之,Dinic是FordFulkerson算法的优化,一次更新多条增广路,dfs那个地方不大好调试,当个模板记住也行.hiahia..
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章