http://poj.org/problem?id=2987 /* 题意:有N个点,每个点有一个权值。有M个关系(A,B),代表A是B的上司。 从这个图里面选择一些点,使得权值最大,若选择了A且A是B的上司,则必须选择B。 */ /* 很典型的最大权闭合图问题。将原问题的图转化为最小割模型。 第二问就是总盈利-总亏损,即Sum{正权值}-MaxFlow。 下面讨论第一问。 由于最小割一定只会出现在(S,I)和(I,T)中,且最小割一定是一个可行方案。 所以,我们只需要求出X集中的点,只要选择这些点,一定可以达到最小割,一定可以得到可行方案 用ISAP算法 */ #include<iostream> using namespace std; #define MAXM 60010 #define MAXN 5010 #define inf 1000000000 struct Edge { int to, cap, flow;//容量与流量 int next, pair;//pair是用来储存相反边 }edge[2*MAXM]; int n, m; int source, dest;//原点,汇点 int cnt, head[MAXN]; //建图过程,每次要建立两条边 void addedge(int from, int to, int w1, int w2) { edge[cnt].to = to; edge[cnt].next = head[from]; edge[cnt].flow = 0; edge[cnt].cap = w1; edge[cnt].pair = cnt + 1; head[from] = cnt++; //另一条边 edge[cnt].to = from; edge[cnt].next = head[to]; edge[cnt].cap = w2; edge[cnt].flow = 0; edge[cnt].pair = cnt - 1; head[to] = cnt++; } int numb[MAXN];//numb[i]用来保存到汇点的距离为i的节点的个数 int dist[MAXN];//dist[i]用来保存节点i到汇点的最少边数 int pre[MAXN];//用来保存增广路径 int curedge[MAXN];//中间变量 __int64 isap() { int cur_flow, u, tmp, neck, i; __int64 maxflow; memset(dist, 0, sizeof(dist)); memset(numb, 0, sizeof(numb)); for(i=1; i<=n; i++) curedge[i] = head[i]; numb[n] = n; maxflow = 0; u = source; while(dist[source] < n) { if(u == dest)//找到一条增广路径 { cur_flow = inf + 1; for(i = source; i!=dest; i = edge[curedge[i]].to) { if(cur_flow > edge[curedge[i]].cap) { neck = i; cur_flow = edge[curedge[i]].cap; } } for(i = source; i != dest; i = edge[curedge[i]].to) { tmp = curedge[i]; edge[tmp].cap -= cur_flow; edge[tmp].flow += cur_flow; tmp = edge[tmp].pair; edge[tmp].cap += cur_flow; edge[tmp].flow -= cur_flow; } maxflow += cur_flow; u = neck; } for(i = curedge[u]; i != -1; i = edge[i].next) { if(edge[i].cap > 0 && dist[u] == dist[edge[i].to] + 1) break; } if(i != -1) { curedge[u] = i; pre[edge[i].to] = u; u = edge[i].to; } else { if(0 == --numb[dist[u]]) break; curedge[u] = head[u]; for(tmp = n, i = head[u]; i!=-1; i=edge[i].next) { if(edge[i].cap > 0) tmp = tmp < dist[edge[i].to]?tmp:dist[edge[i].to]; } dist[u] = tmp+1; ++numb[dist[u]]; if(u!=source) u = pre[u]; } } return maxflow; } int v[MAXN]; void dfs(int u) { if(u == dest) return; v[u] = 1; int i; for(i = head[u]; i!=-1; i=edge[i].next) if(edge[i].cap > 0 && !v[edge[i].to]) dfs(edge[i].to); } int main() { // freopen("in.txt", "r", stdin); int i, j, w, ans = 0; __int64 temp, sum = 0; scanf("%d %d", &n, &m); source = n+1; dest = n+2; memset(head, -1, sizeof(head)); cnt = 1; for(i=1; i<=n; i++) { scanf("%d", &w); if(w>0) { sum += w; addedge(source, i, w, 0); } else { addedge(i, dest, -w, 0); } } while(m--) { scanf("%d %d", &i, &j); addedge(i, j, inf, 0); } n += 2; temp = isap(); memset(v, 0, sizeof(v)); dfs(source); n -= 2; for(i=1; i<=n; i++) { if(v[i]) ans++; } printf("%d %I64d/n", ans, sum-temp); return 0; }