题目链接:http://poj.org/problem?id=2987
解析:最大权闭合子图,利用网络流求解
证明见如下链接http://hi.baidu.com/jjxtuhzcvfbfhzq/item/591b53cfa538bb7bced4f889
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#define min(x,y) x<y?x:y
#define MEM(x,y) memset(x,y,sizeof(x))
#define Max 1000000000
#define INF 6100
using namespace std;
struct edge
{
int x; //该边的末点
long long v; //边权
int id; //代表该边的反向边在边集中的下标
}e[20*INF]; //注意总共有大概60000*2条边需要存进数组
vector<int>vec[INF]; //vec[i]存由i出发的边在边集中的下标
int n,m,dis[INF]; //dis[]用来存储层数
int num,cnt;
long long ans,sum;
int vis[INF]; //用来在求共有开除的人的个数的时候用来标记该点是否走过
bool bfs(int start,int end)
{
MEM(dis,-1);
queue<int>Q;
dis[start] = 0;
Q.push(start);
while(!Q.empty())
{
int temp = Q.front();
Q.pop();
int len = vec[temp].size();
for(int i = 0; i < len; i++)
{
int t = vec[temp][i]; //由temp出发的边集的下标
if(e[t].v > 0 && dis[e[t].x] == -1) //如果还没分层呢
{
dis[e[t].x] = dis[temp]+1;
Q.push(e[t].x);
}
}
}
if(dis[end] <= 0)
return false;
else
return true;
}
long long dfs(int i,long long flow)
{
if(i == n+1 || flow == 0) //优化
return flow;
int dt=flow;
int len = vec[i].size();
for(int j = 0 ;j < len ; j ++)
{
int t = vec[i][j];
long long a;
if(e[t].v> 0 && (dis[e[t].x] == dis[i] + 1) ) //只能递归下一层的
{
a = dfs(e[t].x,min(dt,e[t].v));
e[t].v -= a;
e[e[t].id].v += a;
dt-=a; //dt代表当循环进行到现在的时候,还剩多少流量,所以要减去每条路的最大流
if(dt==0) break;
}
}
return flow-dt; //dt是这个子图遍历完后还剩多少流量,flow-dt就是该子图的最大流
}
void dinic(int start,int end) //核心思想:用bfs分层,然后用dfs遍历。与EK相比,优化在于,EK每次bfs都只找
//到和增广一个边,Dinic是每次bfs分层,dfs找到并增广多条边
{
while(1)
{
if(bfs(start,end) == false) //如果没有分层图了,算法结束
break;
ans += dfs(start,Max);
}
}
void addedge(int start,int end,long long v)
{
e[cnt].v = v; //正向边
e[cnt].x = end;
e[cnt].id = cnt+1;
vec[start].push_back(cnt);
cnt++;
e[cnt].v = 0; //反向边,权值为0
e[cnt].x = start;
e[cnt].id = cnt - 1;
vec[end].push_back(cnt);
cnt++;
}
void num_dfs(int x) //求共开除几人,只要能到的点,都是没开除
{
vis[x] = 1;
int len = vec[x].size();
for(int i = 0; i <len ; i ++)
{
int t = vec[x][i];
if(e[t].v > 0 && vis[e[t].x] == 0)
{
num++;
num_dfs(e[t].x);
}
}
}
int main()
{
while(scanf("%d%d",&n,&m) != EOF)
{
MEM(e,0);
for(int i = 0; i <= n;i++)
vec[i].clear();
sum = 0;
cnt = 0;
for(int i = 1 ; i <= n;i++)
{
int a;
scanf("%d",&a);
if(a > 0)
{
addedge(0,i,a);
sum += a;
}
else
addedge(i,n+1,-a);
}
for(int i = 1; i <= m ; i ++)
{
int a,b;
scanf("%d%d",&a,&b);
addedge(a,b,Max);
}
ans = 0;
num = 0;
dinic(0,n+1);
MEM(vis,0);
num_dfs(0);
printf("%d %lld\n",num,sum-ans);
}
return 0;
}