POJ 2987 Firing

来源:互联网 发布:淘宝试用中心要钱吗 编辑:程序博客网 时间:2024/06/05 06:24

题目链接: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 6100using 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;elsereturn 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;//反向边,权值为0e[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;}elseaddedge(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;}


 

原创粉丝点击