【BZOJ 1497】 [NOI2006]最大获利

【链接】 我是链接,点我呀:)
【题意】


在这里输入题意

【题解】


最大权闭合子图的模板题。
每个人对两个物品有依赖。
则相当于m+n个点
这m个点是m个用户。
每个用户的权值是正的,为这个用户获得的利益。
然后每个基站对应n个点。这n个点上的权值都是负数的。
表示代价。

然后就是最大权闭合子图问题了。

套一个网络流模型

源点s和每个人连流量为利益的边。
每个基站和汇点t连流量为代价的绝对值的边。
用户和基站对应的边。流量改为正无穷。

这样这个网络的每个割(S,T)中 集合S除去汇点s剩下的点。
就是原图的一个闭合子图了。
(而且会发现每个闭合子图都能由一个割,一一对应

分析一下会发现当求得最小割的时候。
得到的闭合子图权值是最大的。
而且这个闭合子图的权值就是所有正权的权值和减去最小割。

参考 胡伯涛 《最小割模型在信息学竞赛中的应用》

【代码】

#include<bits/stdc++.h>
using namespace std;

const int N = 5000;
const int M = 50000;
const int INF = 100*5000+50000*100 + 10;
const int s = 0;

struct node{
    int v,w,nex;
}edge[(M*3+N+100)*2];

int n,m,w[N+10],head[N+M+100],cur[N+M+100],k,d[N+M+100],t;
int Q[N+M+100];

void addEdge(int u,int v,int w){
    edge[k].v = v;
    edge[k].w = w;
    edge[k].nex = head[u];
    head[u] = k++;

    edge[k].v = u;
    edge[k].w = 0;
    edge[k].nex = head[v];
    head[v] = k++;
}

int bfs()
{
    memset(d, 0, sizeof(d));
    d[s] = 1;
    int frnt = 0, rear = 0;
    Q[rear++] = s;
    while(frnt != rear)
    {
        int u = Q[frnt++];
        if(u == t) return 1;
        for(int i = head[u]; i != -1; i = edge[i].nex)
        {
            int to = edge[i].v, w = edge[i].w;
            if(w && d[to] == 0)
            {
                d[to] = d[u] + 1;
                if(to == t) return 1;
                Q[rear++] = to;
            }
        }
    }
    return 0;
}

int dfs(int u, int maxflow)
{
    if(u == t || !maxflow) return maxflow;
    int ret = 0;
    for(int& i = cur[u]; i != -1; i = edge[i].nex)
    {
        int to = edge[i].v, w = edge[i].w;
        if(w && d[to] == d[u]+1)
        {
            int f = dfs(to, min(maxflow-ret, w));
            edge[i].w -= f;
            edge[i^1].w += f;
            ret += f;
            if(ret == maxflow) return ret;
        }
    }
    return ret;
}

int Dicnic(){
    int ans = 0;
    while (bfs()==1){
        memcpy(cur,head,sizeof(head));
        ans+=dfs(0,INF);
    }
    return ans;
}

int main()
{
    memset(head,255,sizeof head);
    cin >> n >> m;
    t = n+m+1;
    for (int i = 1;i <= n;i++) cin >> w[i];
    int ans = 0;
    for (int i = 1;i <= m;i++){
        int x,y,z;
        cin >> x >> y >> z;
        addEdge(i,m+x,INF);addEdge(i,m+y,INF);
        addEdge(0,i,z);
        ans+=z;
    }
    for (int i = 1;i <= n;i++) addEdge(m+i,m+n+1,w[i]);
    cout<<ans-Dicnic()<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/AWCXV/p/9012337.html
今日推荐