题目描述:
Description 新的技术正冲击着手机通讯市场,对于各大运营商来说,这既是机遇,更是挑战。THU集团旗下的CS&T通讯公司在新一代通讯技术血战的前夜,需要做太多的准备工作,仅就站址选择一项,就需要完成前期市场研究、站址勘测、最优化等项目。在前期市场调查和站址勘测之后,公司得到了一共N个可以作为通讯信号中转站的地址,而由于这些地址的地理位置差异,在不同的地方建造通讯中转站需要投入的成本也是不一样的,所幸在前期调查之后这些都是已知数据:建立第i个通讯中转站需要的成本为Pi(1≤i≤N)。另外公司调查得出了所有期望中的用户群,一共M个。关于第i个用户群的信息概括为Ai, Bi和Ci:这些用户会使用中转站Ai和中转站Bi进行通讯,公司可以获益Ci。(1≤i≤M, 1≤Ai, Bi≤N) THU集团的CS&T公司可以有选择的建立一些中转站(投入成本),为一些用户提供服务并获得收益(获益之和)。那么如何选择最终建立的中转站才能让公司的净获利最大呢?(净获利 = 获益之和 - 投入成本之和) Input 输入文件中第一行有两个正整数N和M 。第二行中有N个整数描述每一个通讯中转站的建立成本,依次为P1, P2, …, PN 。以下M行,第(i + 2)行的三个数Ai, Bi和Ci描述第i个用户群的信息。所有变量的含义可以参见题目描述。 Output 你的程序只要向输出文件输出一个整数,表示公司可以得到的最大净获利。 Sample Input 5 5 1 2 3 4 5 1 2 3 2 3 4 1 3 3 1 4 2 4 5 3 Sample Output 4 HINT 【样例说明】 选择建立1、2、3号中转站,则需要投入成本6,获利为10,因此得到最大收益4。 【评分方法】 本题没有部分分,你的程序的输出只有和我们的答案完全一致才能获得满分,否则不得分。 【数据规模和约定】 80%的数据中:N≤200,M≤1 000。 100%的数据中:N≤5 000,M≤50 000,0≤Ci≤100,0≤Pi≤100。這個題難點在建模。
所求結果即為所有用戶帶來收益的總和減去最大流(證明略)。
Accode:
#include <cstdio> #include <cstring> #include <cstdlib> #include <bitset> const char fi[] = "profit.in"; const char fo[] = "profit.out"; const int maxN = 5010; const int maxM = 50010; const int MAX = 0x3fffff00; const int MIN = -MAX; struct Edge {int u, v, f; }; Edge edge[(3 * maxM + maxN) << 1]; int fir[maxM + maxN]; int cnt[maxM + maxN], d[maxM + maxN]; int N, M, n, p, x, y, c, ans, m; void init_file() { freopen(fi, "r", stdin); freopen(fo, "w", stdout); } inline void insert(int u, int v, int f) { edge[++m].u = u; edge[m].v = v; edge[m].f = f; } inline int cmp(const void *a, const void *b) { if (((Edge *)a) -> u != ((Edge *)b) -> u) return ((Edge *)a) -> u - ((Edge *)b) -> u; return ((Edge *)a) -> v - ((Edge *)b) -> v; } void readdata() { scanf("%d%d", &N, &M); n = N + M + 2; for (int i = 1; i < N + 1; ++i) { scanf("%d", &p); insert(1, i + 1, p); insert(i + 1, 1, 0); } for (int i = 1; i < M + 1; ++i) { scanf("%d%d%d", &x, &y, &c); insert(x + 1, i + N + 1, MAX); insert(i + N + 1, x + 1, 0); insert(y + 1, i + N + 1, MAX); insert(i + N + 1, y + 1, 0); insert(i + N + 1, n, c); insert(n, i + N + 1, 0); ans += c; } qsort(edge + 1, m, sizeof(edge[0]), cmp); for (int i = m; i; --i) fir[edge[i].u] = i; } inline int Find(int u, int v) { int L = fir[u], R = m; while (L <= R) { int Mid = (L + R) >> 1; if (edge[Mid].u == u && edge[Mid].v == v) return Mid; if (edge[Mid].u > u || edge[Mid].v > v) R = Mid - 1; else L = Mid + 1; } } int Sap(int u, int Lim) { if (u == n) return Lim; int tmp = 0; for (int i = fir[u]; i < m + 1; ++i) { if (edge[i].u != u) break; int v = edge[i].v; if (edge[i].f > 0 && d[u] == d[v] + 1) { int k = Sap(v, std::min(Lim - tmp, edge[i].f)); if (k <= 0) continue; edge[i].f -= k; edge[Find(v, u)].f += k; if ((tmp += k) == Lim) return tmp; } } if (d[1] >= n) return tmp; if ((--cnt[d[u]]) <= 0) d[1] = n; ++cnt[++d[u]]; return tmp; } void work() { cnt[0] = n; while (d[1] < n) ans -= Sap(1, MAX); printf("%d", ans); } int main() { init_file(); readdata(); work(); exit(0); }
第二次做:
/****************************\ * @prob: NOI2006 profit * * @auth: Wang Junji * * @stat: Accepted. * * @date: June. 2nd, 2012 * * @memo: 最大流 * \****************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 60010, INF = 0x3f3f3f3f; struct Edge { int v, f; Edge *next, *op; Edge() {} Edge(int v, int f, Edge *next): v(v), f(f), next(next) {} } *edge[maxN]; int n, m, N, S, T, ans; inline static void Ins(int u, int v, int f) { edge[u] = new Edge(v, f, edge[u]); edge[v] = new Edge(u, 0, edge[v]); edge[u] -> op = edge[v]; edge[v] -> op = edge[u]; return; } inline static int getint() { int res = 0; char tmp; while (!isdigit(tmp = getchar())); do res = (res << 3) + (res << 1) + tmp - '0'; while (isdigit(tmp = getchar())); return res; } inline static int Sap() { static Edge *cur[maxN], *p; int ans = 0; static int d[maxN], cnt[maxN], pre[maxN]; memcpy(cur, edge, sizeof edge); cnt[0] = N; pre[S] = S; for (int u = S; d[S] < N;) { if (u == T) { int max_flow = INF; for (int i = S; i - T; i = cur[i] -> v) max_flow = std::min(max_flow, cur[i] -> f); for (int i = S; i - T; i = cur[i] -> v) cur[i] -> f -= max_flow, cur[i] -> op -> f += max_flow; ans += max_flow, u = S; } for (p = cur[u]; p; p = p -> next) if (p -> f > 0 && d[u] == d[p -> v] + 1) { cur[u] = p, pre[p -> v] = u, u = p -> v; break; } if (!p) { if (--cnt[d[u]] == 0) break; cur[u] = edge[u]; int min_d = N; for (Edge *p = edge[u]; p; p = p -> next) if (p -> f > 0) min_d = std::min(min_d, d[p -> v]); ++cnt[d[u] = min_d + 1], u = pre[u]; } } return ans; } int main() { freopen("profit.in", "r", stdin); freopen("profit.out", "w", stdout); m = getint(); n = getint(); S = n + m + 1, N = T = n + m + 2; for (int i = 1; i < m + 1; ++i) Ins(i + n, T, getint()); for (int i = 1, tmp; i < n + 1; ++i) Ins(i, getint() + n, INF), Ins(i, getint() + n, INF), Ins(S, i, tmp = getint()), ans += tmp; printf("%d\n", ans -= Sap()); return 0; }