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..
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章