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..