【bzoj1497】NOI2006最大获利

Problem

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。

Solution

第i个基站造价为c[i],则i号点的权值val[i]=-c[i]
第i条边(u,v)收益为w[i],则新建(n+i)号点,连边(n+i,u)和(n+i,v),val[n+i]=w[i]
这样题目就转化为求最大权闭合子图
最大权闭合子图:给定带权图G(权值可正可负),求一个权和最大的点集,使得起点在该点集中的任意弧,终点也在该点集中
建模与解决:新增附加源s和附加汇t,从s向每一个正权点连一条边,容量为权值;从每一个负权点向t连一条边,容量为权值的相反数;中间的边容量为无穷大。求出最小割(S,T)以后,S集除去附加源就是最大权闭合子图。

Code

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define red(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define ld long double

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(!isdigit(c)) { if (c == '-') f = -1; c = getchar(); }
    while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

const int inf = 1000000000;
const int N = 300000;
struct edge {
    int from, to, cap, flow;
};
vector <edge> e;
vector <int> g[N];
int d[N], cur[N], val[N];
bool vis[N];
int n, m, s, t;

void build(int from, int to, int cap) {
    e.push_back((edge){from, to, cap, 0});
    e.push_back((edge){to, from, 0, 0});
    int m = e.size();
    g[from].push_back(m - 2);
    g[to].push_back(m - 1);
}

bool bfs() {
    memset(vis, 0, sizeof(vis));
    queue<int> q; q.push(s);
    d[s] = 0; vis[s] = 1;
    while(!q.empty()) {
        int x = q.front(); q.pop();
        for(int i = 0; i < g[x].size(); i++) {
            edge& now = e[g[x][i]];
            if (!vis[now.to] && now.cap > now.flow) {
                vis[now.to] = 1;
                d[now.to] = d[x] + 1;
                q.push(now.to);
            }
        }
    }
    return vis[t];
}

int dfs(int x, int a) {
    if (x == t || a == 0) return a;
    int flow = 0, f;
    for(int& i = cur[x]; i < g[x].size(); i++) {
        edge& now = e[g[x][i]];
        if (d[x] + 1 == d[now.to] && (f = dfs(now.to, min(a, now.cap - now.flow))) > 0) {
            now.flow += f;
            e[g[x][i] ^ 1].flow -= f;
            flow += f;
            a -= f;
            if (a == 0) break;
        }
    }
    return flow;
}

void Dinic(int s, int t) {
    int flow = 0;
    while(bfs()) {
        memset(cur, 0, sizeof(cur));
        flow += dfs(s, inf);
    }
}

int main() {
    n = read(); m = read();
    rep(i, 1, n) val[i] = (-1) * read();
    rep(i, 1, m) {
        int s = read(), t = read(), w = read();
        val[++n] = w;
        build(n, s, inf);
        build(n, t, inf);
    }
    s = 0; t = n + 1;
    rep(i, 1, n) {
        if (val[i] > 0) build(s, i, val[i]);
        else build(i, t, -val[i]);
    }
    Dinic(s, t);
    int ans = 0;
    rep(i, 1, n) if (vis[i]) ans += val[i];
    printf("%d\n", ans);
    return 0;
}

尾声

睿姐只顾约X,不理我
Bitch is Bitch
我去四川玩泥巴

End.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值