数模常用算法——图论算法简介&代码实现(下)

4.2 二分最大权匹配 KM算法模板
类似题目链接:http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=2073

陈叔叔题解:http://www.cnblogs.com/njczy2010/p/4384505.html

KM模板(注:如果求的是最小值,main函数初始时取负值,并且out函数输出-KM()即可):

#include <cstdio>  
#include <cstring>  
#include <stack>  
#include <vector>  
#include <algorithm>  
#include <map>  
#include <string>  
#include <queue>  
#include <cmath>  
  
#define ll long long  
int const N = 35;  
int const M = 100005;  
int const INF = 0x3f3f3f3f;  
ll const mod = 1000000007;  
  
using namespace std;  
  
int T;  
int n;  
int nx,ny;       //两边的点数  
int g[N][N];    //二分图描述  
int linker[N],lx[N],ly[N];      //y中各点匹配状态,x,y中的点标号  
int slack[N];  
bool visx[N],visy[N];  
  
bool DFS(int x)  
{  
    visx[x] = true;  
    for(int y = 0;y < ny;y++)  
    {  
        if(visy[y]) continue;  
        int tmp = lx[x] + ly[y] -g[x][y];  
        if(tmp == 0)  
        {  
            visy[y] = true;  
            if(linker[y] == -1 || DFS(linker[y]))  
            {  
                linker[y] = x;  
                return true;  
            }  
        }  
        else if(slack[y] > tmp)  
            slack[y] = tmp;  
    }  
    return false;  
}  
  
int KM()  
{  
    memset(linker,-1,sizeof(linker));  
    memset(ly,0,sizeof(ly));  
    for(int i = 0;i < nx;i++)  
    {  
        lx[i] = -INF;  
        for(int j = 0;j < ny;j++)  
            if(g[i][j] > lx[i])  
                lx[i] = g[i][j];  
    }  
    for(int x =0;x < nx;x++)  
    {  
        for(int i = 0;i < ny ;i++)  
            slack[i] = INF;  
        while(true)  
        {  
            memset(visx,false,sizeof(visx));  
            memset(visy,false,sizeof(visy));  
            if(DFS(x)) break;  
            int d = INF;  
            for(int i = 0;i < ny;i++)  
                if(!visy[i] && d > slack[i])  
                    d = slack[i];  
            for(int i = 0 ; i < nx ;i++)  
                if(visx[i])  
                    lx[i] -= d;  
            for(int i = 0 ; i < ny ;i++)  
            {  
                if(visy[i]) ly[i] += d;  
                else slack[i] -= d;  
            }  
        }  
    }  
    int res = 0;  
    for(int i = 0;i < ny ;i++)  
        if(linker[i] != -1)  
            res += g[ linker[i] ][i];  
    return res;  
}  
  
void ini()  
{  
    scanf("%d",&n);  
    int i,j;  
    for(i = 0;i < n;i++){  
        for(j = 0;j < n;j++)  
            scanf("%d",&g[i][j]);  
    }  
    nx = ny =n;  
}  
  
void solve()  
{  
  
}  
  
void out()  
{  
    printf("%d\n",KM());  
}  
  
int main()  
{  
    //freopen("data.in","r",stdin);  
    //freopen("data.out","w",stdout);  
    scanf("%d",&T);  
    //for(int cnt=1;cnt<=T;cnt++)  
    while(T--)  
    //while(scanf("%d%d%d",&a,&b,&n)!=EOF)  
    {  
        ini();  
        solve();  
        out();  
    }  
}

4.3 最小费用最大流

大大2135题解链接:poj2135 - 金海峰 - 博客园

算法模板:POJ2135可转化,有点像中国邮递员问题

#include <iostream>  
#include <cstdio>  
#include <cstdlib>  
#include <cstring>  
using namespace std;  
  
#define typef int  
#define typec int  
#define maxn 1005  
#define maxm 10005  
#define N maxn + 2  
#define E maxm * 4 + 4  
const typef inff = 0x3f3f3f3f;  
const typec infc = 0x3f3f3f3f;  
  
struct network  
{  
    int nv, ne, pnt[E], nxt[E];  
    int vis[N], que[N], head[N], pv[N], pe[N];  
    typef flow, cap[E];  
    typec cost, dis[E], d[N];  
    void addedge(int u, int v, typef c, typec w)  
    {  
        pnt[ne] = v;  
        cap[ne] = c;  
        dis[ne] = +w;  
        nxt[ne] = head[u];  
        head[u] = (ne++);  
        pnt[ne] = u;  
        cap[ne] = 0;  
        dis[ne] = -w;  
        nxt[ne] = head[v];  
        head[v] = (ne++);  
    }  
    int mincost(int src, int sink)  
    {  
        int i, k, f, r;  
        typef mxf;  
        for (flow = 0, cost = 0;;)  
        {  
            memset(pv, -1, sizeof(pv));  
            memset(vis, 0, sizeof(vis));  
            for (i = 0; i < nv; ++i)  
                d[i] = infc;  
            d[src] = 0;  
            pv[src] = src;  
            vis[src] = 1;  
            for (f = 0, r = 1, que[0] = src; r != f;)  
            {  
                i = que[f++];  
                vis[i] = 0;  
                if (N == f)  
                    f = 0;  
                for (k = head[i]; k != -1; k = nxt[k])  
                    if (cap[k] && dis[k] + d[i] < d[pnt[k]])  
                    {  
                        d[pnt[k]] = dis[k] + d[i];  
                        if (0 == vis[pnt[k]])  
                        {  
                            vis[pnt[k]] = 1;  
                            que[r++] = pnt[k];  
                            if (N == r)  
                                r = 0;  
                        }  
                        pv[pnt[k]] = i;  
                        pe[pnt[k]] = k;  
                    }  
            }  
            if (-1 == pv[sink])  
                break;  
            for (k = sink, mxf = inff; k != src; k = pv[k])  
                if (cap[pe[k]] < mxf)  
                    mxf = cap[pe[k]];  
            flow += mxf;  
            cost += d[sink] * mxf;  
            for (k = sink; k != src; k = pv[k])  
            {  
                cap[pe[k]] -= mxf;  
                cap[pe[k] ^ 1] += mxf;  
            }  
        }  
        return cost;  
    }  
    void build(int v, int e)  
    {  
        nv = v;  
        ne = 0;  
        memset(head, -1, sizeof(head));  
        int x, y;  
        typec w;  
        for (int i = 0; i < e; ++i)  
        {  
            scanf("%d%d%d", &x, &y, &w);  
            addedge(x, y, 1, w);// add arc (u->v, f, w)  
            addedge(y, x, 1, w);  
        }  
        addedge(0, 1, 2, 0);  
        addedge(v - 2, v - 1, 2, 0);  
    }  
} g;  
  
int n, m;  
  
int main()  
{  
    //freopen("t.txt", "r", stdin);  
    scanf("%d%d", &n, &m);  
    g.build(n + 2, m);  
    printf("%d\n", g.mincost(0, n + 1));  
    return 0;  
}  
/* 
4 5 
1 2 1 
2 3 1 
3 4 1 
1 3 2 
2 4 2 
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值