zoukankan      html  css  js  c++  java
  • hdu 3488 Tour KM

    题目大意是有n个点,m条边,每条边有一个权值,问将所有点划分为圈的最小花费(每个点都在且仅在一个圈上)。

    因为每个顶点只出现一次,那么每个顶点只关联两个顶点入度顶点和出度顶点,所以构造二分图,将一个点u拆成u,u'。那么对于这个二分图如果存在着完美匹配的话,那么原图中一定存在若干个环,环中包含每个顶点,对于权值之和最小,只需求最小权匹配即可。

    #include <stdio.h>
    #include <string.h>
    #define M 500
    #define inf 100000000
    
    int m;
    int nx,ny;
    int link[M],lx[M],ly[M],slack[M];    //lx,ly为顶标,nx,ny分别为x点集y点集的个数
    int visx[M],visy[M],w[M][M];
    
    int DFS(int x)
    {
        visx[x] = 1;
        for (int y = 1;y <= ny;y ++)
        {
            if (visy[y])
                continue;
            int t = lx[x] + ly[y] - w[x][y];
            if (t == 0)       //
            {
                visy[y] = 1;
                if (link[y] == -1||DFS(link[y]))
                {
                    link[y] = x;
                    return 1;
                }
            }
            else if (slack[y] > t)  //不在相等子图中slack 取最小的
                slack[y] = t;
        }
        return 0;
    }
    int KM()
    {
        int i,j;
        memset (link,-1,sizeof(link));
        memset (ly,0,sizeof(ly));
        for (i = 1;i <= nx;i ++)            //lx初始化为与它关联边中最大的
            for (j = 1,lx[i] = -inf;j <= ny;j ++)
                if (w[i][j] > lx[i])
                    lx[i] = w[i][j];
    
        for (int x = 1;x <= nx;x ++)
        {
            for (i = 1;i <= ny;i ++)
                slack[i] = inf;
            while (1)
            {
                memset (visx,0,sizeof(visx));
                memset (visy,0,sizeof(visy));
                if (DFS(x))     //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广
                    break;  //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。
                            //方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,
                            //所有在增广轨中的Y方点的标号全部加上一个常数d
                int d = inf;
                for (i = 1;i <= ny;i ++)
                    if (!visy[i]&&d > slack[i])
                        d = slack[i];
                for (i = 1;i <= nx;i ++)
                    if (visx[i])
                        lx[i] -= d;
                for (i = 1;i <= ny;i ++)  //修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d
                    if (visy[i])
                        ly[i] += d;
                    else
                        slack[i] -= d;
            }
        }
        int res = 0;
        for (i = 1;i <= ny;i ++)
            if (link[i] > -1)
                res += w[link[i]][i];
        return res;
    }
    
    int main()
    {
        int cas;
        int i,j;
        int u,v,z;
        scanf("%d",&cas);
        while(cas--)
        {
            scanf("%d%d",&nx,&m);
            for(i=1;i<=2*nx+10;i++)
                for(j=1;j<=2*nx+10;j++)
                    w[i][j]=-inf;
            ny=nx;
            for(i=1;i<=m;i++)
            {
                scanf("%d%d%d",&u,&v,&z);
                if(-z>w[u][v])
                    w[u][v]=-z;
            }
            int ans=KM();
            printf("%d
    ",-ans);
        }
        return 0;
    }
    


     

  • 相关阅读:
    Leetcode Unique Binary Search Trees
    Leetcode Decode Ways
    Leetcode Range Sum Query 2D
    Leetcode Range Sum Query
    Leetcode Swap Nodes in Pairs
    Leetcode Rotate Image
    Leetcode Game of Life
    Leetcode Set Matrix Zeroes
    Leetcode Linked List Cycle II
    CF1321A
  • 原文地址:https://www.cnblogs.com/vermouth/p/3710159.html
Copyright © 2011-2022 走看看