zoukankan      html  css  js  c++  java
  • 网络流之最小点权覆盖和最大点权独立集学习

    转载:http://yzmduncan.iteye.com/blog/1149057    
    二分图最小点覆盖和最大独立集都可以转化为最大匹配求解。在这个基础上,把每个点赋予一个非负的权值,这两个问题就转化为:二分图最小点权覆盖和二分图最大点权独立集。
     
        二分图最小点权覆盖
        从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。
    建模:
        原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t,将s和x集合中的点相连,容量为该点的权值;将y中的点同t相连,容量为该点的权值。在新图上求最大流,最大流量即为最小点权覆盖的权值和。
     
    二分图最大点权独立集
        在二分图中找到权值和最大的点集,使得它们之间两两没有边。其实它是最小点权覆盖的对偶问题。答案=总权值-最小点覆盖集。具体证明参考胡波涛的论文。
     
    例:HDU1569
    题意:一个m*n的棋盘,每个格子都有一个权值,从中取出某些数,使得任意两个数所在的格子没有公共边,并且所取去出的数和最大。求这个最大的值。
    解:
        将格子染色成二分图,显然是求二分图的最大点权独立集。将问题转化为二分图最小点权覆盖来求解,最终结果=总权和-最大流。
    /*
    最大点权独立集:
    转化为最小点权覆盖问题,最大点权独立集=总权值-最小点权覆盖集
    最小点权覆盖:
    设立源点s和t,s连边到点i,容量为i点的权值;点j连边到t,容量为j点权值;原二分图中的边容量为INF,求最大流即为最小点权覆盖。
    */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int INF = 0x7fffffff;
    const int maxv = 2600;
    const int maxe = 1000000;
    int n,m;
    int g[55][55];
    struct Edge
    {
        int v;
        int next;
        int flow;
    };
    Edge e[maxe];
    int head[maxv],edgeNum;
    int now[maxv],d[maxv],vh[maxv],pre[maxv],preh[maxv];
    int start,end;
    
    void addEdge(int a,int b,int c)
    {
        e[edgeNum].v = b;
        e[edgeNum].flow = c;
        e[edgeNum].next = head[a];
        head[a] = edgeNum++;
        e[edgeNum].v = a;
        e[edgeNum].flow = 0;
        e[edgeNum].next = head[b];
        head[b] = edgeNum++;
    }
    
    void Init()
    {
        edgeNum = 0;
        memset(head,-1,sizeof(head));
        memset(d,0,sizeof(d));
    }
    
    int sap(int s,int t,int n)       //源点,汇点,结点总数
    {
        int i,x,y;
        int f,ans = 0;
        for(i = 0; i < n; i++)
            now[i] = head[i];
        vh[0] = n;
        x = s;
        while(d[s] < n)
        {
            for(i = now[x]; i != -1; i = e[i].next)
                if(e[i].flow > 0 && d[y=e[i].v] + 1 == d[x])
                    break;
                if(i != -1)
                {
                    now[x] = preh[y] = i;
                    pre[y] = x;
                    if((x=y) == t)
                    {
                        for(f = INF,i=t; i != s; i = pre[i])
                            if(e[preh[i]].flow < f)
                                f = e[preh[i]].flow;
                        ans += f;
                        do
                        {
                            e[preh[x]].flow -= f;
                            e[preh[x]^1].flow += f;
                            x = pre[x];
                        }while(x!=s);
                    }
                }
                else
                {
                    if(!--vh[d[x]])
                        break;
                    d[x] = n;
                    for(i=now[x]=head[x]; i != -1; i = e[i].next)
                    {
                        if(e[i].flow > 0 && d[x] > d[e[i].v] + 1)
                        {
                            now[x] = i;
                            d[x] = d[e[i].v] + 1;
                        }
                    }
                    ++vh[d[x]];
                    if(x != s)
                        x = pre[x];
                }
        }
        return ans;
    }
    
    
    void build()
    {
        int i,j;
        for(i = 1; i <= m; i++)
        {
            for(j = 1; j <= n; j++)
            {
                int t = (i-1)*n+j;
                if((i+j)%2)
                {
                    addEdge(start,t,g[i][j]);
                    if(i>1)
                        addEdge(t,t-n,INF);
                    if(i<m)
                        addEdge(t,t+n,INF);
                    if(j>1)
                        addEdge(t,t-1,INF);
                    if(j<n)
                        addEdge(t,t+1,INF);
                }
                else
                    addEdge(t,end,g[i][j]);
            }
        }
    }
    
    int main()
    {
        int i,j;
        int result;
        while(scanf("%d %d",&m,&n) != EOF)
        {
            result = 0;
            Init();
            for(i = 1; i <= m; i++)
            {
                for(j = 1; j <= n; j++)
                {
                    scanf("%d",&g[i][j]);
                    result += g[i][j];
                }
            }
            start = 0;
            end = n*m + 1;
            build();
            printf("%d
    ",result-sap(start,end,end+1));
        }
        return 0;
    }
    

  • 相关阅读:
    高精度乘法
    阶乘
    高精度减法
    高精度加法
    曹冲养猪
    采药2
    nginx.conf详解
    系统盘脚本扩容
    IDEA中编写脚本并运行shell脚本
    常用的pdf工具
  • 原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410661.html
Copyright © 2011-2022 走看看