【網絡流】【NOI2006】最大獲利

题目描述:

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。
這個題難點在建模。

建立一個有N + M + 2個點,3M + N條邊的圖。
其中,有一個超級源S,N個中轉站,M個用戶和一個超级汇T;S到每個中轉站連一條以該中轉站成本為權值的邊,從每一個中轉站到每一個能夠與它通訊的用戶連一條以正無窮為權值的邊,再從每一個用戶到超級匯連一條以該用戶帶來的收益為權值的邊。

所求結果即為所有用戶帶來收益的總和減去最大流(證明略)。

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;
}

你可能感兴趣的:(BI,SAP,input,insert,通讯,output)