hdu 3879 最小割模型

Base Station

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 65768/32768 K (Java/Others)
Total Submission(s): 1965    Accepted Submission(s): 831


Problem Description
A famous mobile communication company is planning to build a new set of base stations. According to the previous investigation, n places are chosen as the possible new locations to build those new stations. However, the condition of each position varies much, so the costs to built a station at different places are different. The cost to build a new station at the ith place is P i (1<=i<=n).

When complete building, two places which both have stations can communicate with each other.

Besides, according to the marketing department, the company has received m requirements. The ith requirement is represented by three integers A i, B i and C i, which means if place A and B i can communicate with each other, the company will get C i profit.

Now, the company wants to maximize the profits, so maybe just part of the possible locations will be chosen to build new stations. The boss wants to know the maximum profits.
 

Input
Multiple test cases (no more than 20), for each test case:
The first line has two integers n (0<n<=5000) and m (0<m<=50000).
The second line has n integers, P1 through Pn, describes the cost of each location.
Next m line, each line contains three integers, A i, B i and C i, describes the ith requirement.
 

Output
One integer each case, the maximum profit of the company.
 

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
 

Author
liulibo
 

Source
 

Recommend
lcy   |   We have carefully selected several similar problems for you:   1532  3572  3657  3081  3549 
 

“用最小的费用将对象划分成两个集合的问题,常常可以转换成最小割后顺利解决” 

这个问题要求的是建基站最大的收益,对应到最小割就应该转换成求解最小成本建站的问题,并将成本对应为图中边的容量。

将要建站的集合记为S,不建站的集合记为T。考虑s-t割,第i个基站建站成本为p[i],建站(属于S集合)则对应一条从第i个点到t点,容量为p[i]的边,不建站(属于T集合)则对应一条从s到i容量为0的边(也就是不连)。

对于a点和b点都建站有收益c,收益c就是成本为-c,但是网络流不适用于有负边的情况,所以要进行适当的转换。将情况反转过过来,一开始就当a和b的都建站有收益c,那么条件在图中就变成了如果他们没有都建站则成本要加上c(即减回收益c),这样边容量就转换成正的。一开记录下所有的收益和sum,最后求出sum-最小成本就是最大收益。所以对于条件(a,b,c),增加一个新节点n,连接s到n容量为c,连接n到a和b各一条容量为inf的边。这样连是因为要考虑,只有a不建站,只有b不建站,a和b都不建站的情况,画个图就容易理解了。

建完图后O(n^2m)的Dinic算法600ms就能过了。这个复杂度其实是上界而已,实际情况中一般不会达到,并且这道题中的图是稀疏图。




#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;

#define maxn 5005
#define maxm 50005
#define inf 0x3f3f3f3f

int n,m;
int s,t;

struct edge
{
    int to,cap,rev;
    edge(int a, int b, int c){to=a;cap=b;rev=c;}
};

vector<edge> g[maxn+maxm];

void add(int u, int v, int cap)
{
    g[u].push_back(edge(v,cap,g[v].size()));
    g[v].push_back(edge(u,0,g[u].size()-1));
}


int level[maxn+maxm];
int itr[maxn+maxm];
void bfs()
{
    queue<int> q;
    while(!q.empty()) q.pop();

    memset(level, -1, sizeof(level));
    q.push(s);
    level[s] = 0;

    while(!q.empty()){
        int u = q.front(); q.pop();

        int v,w;
        for(int i = 0; i < g[u].size(); i++){
            v = g[u][i].to,  w = g[u][i].cap;
            if(level[v]!=-1 || w <= 0) continue;
            level[v] = level[u]+1;
            q.push(v);

            if(v == t) return;
        }
    }
}


int dfs(int u, int f)
{
    if(u == t) return f;

    for(int &i = itr[u]; i < g[u].size(); i++){
        int v = g[u][i].to, &w = g[u][i].cap, rev = g[u][i].rev;
        if(level[v] != level[u]+1 || w <= 0) continue;

        int d;
        if(d=dfs(v, min(f,w))){
            w -= d;
            g[v][rev].cap += d;
            return d;
        }
    }

    return 0;
}

long long Dinic()
{
    long long ret = 0;

    while(1){
        bfs();
        if(level[t] == -1) break;

        memset(itr, 0, sizeof(itr));
        int f;
        while((f = dfs(s, inf))){ret+= f;}
    }

    return ret;
}


int main()
{
    while(scanf("%d%d", &n, &m)==2){
        for(int i = 0; i < maxm+maxn; i++) g[i].clear();

        s = 0; t = n+1;
        int a,b,c;
        for(int i = 1; i <= n; i++){
            scanf("%d", &a);
            add(i, t, a);
        }

        long long ans = 0;
        for(int i = 1;  i <= m; i++){
            scanf("%d%d%d", &a, &b, &c);
            ans += c;
            add(n+1+i,a,inf);
            add(n+1+i,b,inf);
            add(s, n+1+i, c);
        }


        printf("%I64d\n", ans-Dinic());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值