• KM算法入门


    KM算法的基本概念:

    http://baike.baidu.com/view/739278.htm

    http://baike.baidu.com/view/501092.htm

    看这个算法之前,最好先看下匈牙利算法,KM算法 是建立在匈牙利算法基础上实现的

    对于这个算法最有误区的地方,个人感觉还是在  X 集合 -d  和 Y 集合 + d之后 还要进行

    操作,再加上 深搜递归操作  ,理解容易产生误区,在这里我给出一组模板的测试数据来帮助初学者理解

    注意观察: visx[],visy[],lx[],ly[],linky[],在调用中的变化:

    3 4

    0 0 2

    0 1 6

    1  1 7

    2 1  14

    2  2  3

    模板:(O  ^ 4) 

    #define M 505
    #define inf 0x3fffffff
    bool sx[M], sy[M];
    int match[M], w[M][M], n, m, d, lx[M], ly[M];
    //n:左集元素个数; m:右集元素个数
    void init ()
    {
    memset (w, 0, sizeof(w)); //不一定要,求最小值一般要初始化为负无穷!
    }

    bool dfs (int u)
    {
    int v; sx[u] = true;
    for (v = 0; v < m; v++)
    {
    if (!sy[v] && lx[u]+ly[v]==w[u][v])
    {
    sy[v] = true;
    if (match[v] == -1 || dfs (match[v]))
    {
    match[v] = u;
    return true;
    }
    }
    }
    return false;
    }

    int KM ()
    {
    int i, j, k, sum = 0;
    memset (ly, 0, sizeof(ly));
    for (i = 0; i < n; i++)
    {
    lx[i] = -inf;
    for (j = 0; j < m; j++)
    if (lx[i] < w[i][j])
    lx[i] = w[i][j];
    }
    memset (match, -1, sizeof(match));
    for (i = 0; i < n; i++)
    {
    while (1)
    {
    memset (sx, false, sizeof(sx));
    memset (sy, false, sizeof(sy));
    if (dfs (i))
    break;
    d = inf;
    for (j = 0; j < n; j++)
    if (sx[j])
    for (k = 0; k < m; k++)
    if (!sy[k])
    d = min (d, lx[j]+ly[k]-w[j][k]);
    if (d == inf) //找不到完美匹配
    return -1;
    for (j = 0; j < n; j++)
    if (sx[j])
    lx[j] -= d;
    for (j = 0; j < m; j++)
    if (sy[j])
    ly[j] += d;
    }
    }
    for (i = 0; i < m; i++)
    if (match[i] > -1)
    sum += w[match[i]][i];
    return sum;
    }

    改进后的模板(O^3)

    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    //赤裸裸的模板啊。。
    const int maxn = 101;
    const int INF = (1<<31)-1;
    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];
    int nx,ny;
    bool find(int x)
    {
    visx[x] = true;
    for(int y = 0; y < ny; y++)
    {
    if(visy[y])
    continue;
    int t = lx[x] + ly[y] - w[x][y];
    if(t==0)
    {
    visy[y] = true;
    if(linky[y]==-1 || find(linky[y]))
    {
    linky[y] = x;
    return true; //找到增广轨
    }
    }
    else if(slack[y] > t)
    slack[y] = t;
    }
    return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }

    int KM() //返回最优匹配的值
    {
    int i,j;

    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i = 0; i < nx; i++)
    for(j = 0,lx[i] = -INF; j < ny; j++)
    if(w[i][j] > lx[i])
    lx[i] = w[i][j];
    for(int x = 0; x < nx; x++)
    {
    for(i = 0; i < ny; i++)
    slack[i] = INF;
    while(true)
    {
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    if(find(x)) //找到增广轨,退出
    break;
    int d = INF;
    for(i = 0; i < ny; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
    {
    if(!visy[i] && d > slack[i])
    d = slack[i];
    }
    for(i = 0; i < nx; i++)
    {
    if(visx[i])
    lx[i] -= d;
    }
    for(i = 0; i < ny; i++)
    {
    if(visy[i])
    ly[i] += d;
    else
    slack[i] -= d;
    }
    }
    }
    int result = 0;
    for(i = 0; i < ny; i++)
    if(linky[i]>-1)
    result += w[linky[i]][i];
    return result;
    }

    int main()
    {
    // freopen("g:/1.txt","r",stdin);
    while(true)
    {
    scanf("%d%d",&nx,&ny);
    int a,b,c;
    while(scanf("%d%d%d",&a,&b,&c),a+b+c)
    {
    w[a][b]=c;
    }
    printf("%d\n",KM());
    break;
    }
    return 0;
    }

    题目推荐:

    第一题:hdu  2255   奔小康赚大钱

    http://acm.hdu.edu.cn/showproblem.php?pid=2255

    模板不解释

    View Code
    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    //赤裸裸的模板啊。。
    const int maxn = 301;
    const int INF = (1<<31)-1;
    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];
    int n;
    bool find(int x)
    {
    visx[x] = true;
    for(int y = 1; y <=n; y++)
    {
    if(visy[y])
    continue;
    int t = lx[x] + ly[y] - w[x][y];
    if(t==0)
    {
    visy[y] = true;
    if(linky[y]==-1 || find(linky[y]))
    {
    linky[y] = x;
    return true; //找到增广轨
    }
    }
    else if(slack[y] > t)
    slack[y] = t;
    }
    return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }

    int KM() //返回最优匹配的值
    {
    int i,j;

    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i = 1; i <=n; i++)
    {
    lx[i] = -INF;
    for(j = 1; j <=n; j++)
    if(w[i][j] > lx[i])
    lx[i] = w[i][j];
    }

    for(int x = 1; x <=n; x++)
    {
    for(i = 1; i <=n; i++)
    slack[i] = INF;
    while(true)
    {
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    if(find(x)) //找到增广轨,退出
    break;
    int d = INF;
    for(i = 1; i <=n; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
    {
    if(!visy[i] && d > slack[i])
    d = slack[i];
    }
    for(i = 1; i <=n; i++)
    {
    if(visx[i])
    lx[i] -= d;
    }
    for(i = 1; i <=n; i++)
    {
    if(visy[i])
    ly[i] += d;
    else
    slack[i] -= d;
    }
    }
    }
    int result = 0;
    for(i = 1; i <=n; i++)
    if(linky[i]>-1)
    result += w[linky[i]][i];
    return result;
    }

    int main()
    {
    // freopen("g:/1.txt","r",stdin);

    while(scanf("%d",&n)==1)
    {
    int cost;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
    scanf("%d",&cost);
    w[i][j]=cost;
    }
    printf("%d\n",KM());
    }
    return 0;
    }

    第二题:  hdu 1533  Going Home

    http://acm.hdu.edu.cn/showproblem.php?pid=1533

     用 w[i][j] = -w[i][j]建图再套模板 求最大值   输出【-sum】

    View Code
    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    int fx,fy;
    const int maxn = 301;
    const int INF = 0xffffff;
    struct node
    {
    int x,y;
    }sx[maxn],sy[maxn];
    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];

    bool find(int x)
    {
    visx[x] = true;
    for(int y = 1; y <=fy; y++)
    {
    if(visy[y])
    continue;
    int t = lx[x] + ly[y] - w[x][y];
    if(t==0)
    {
    visy[y] = true;
    if(linky[y]==-1 || find(linky[y]))
    {
    linky[y] = x;
    return true; //找到增广轨
    }
    }
    else if(slack[y] > t)
    slack[y] = t;
    }
    return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }

    int KM() //返回最优匹配的值
    {
    int i,j;

    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i = 1; i <=fx; i++)
    {
    lx[i] = -INF;
    for(j = 1; j <=fy; j++)
    if(w[i][j] > lx[i])
    lx[i] = w[i][j];
    }

    for(int x = 1; x <=fx; x++)
    {
    for(i = 1; i <=fy; i++)
    slack[i] = INF;
    while(true)
    {
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    if(find(x)) //找到增广轨,退出
    break;
    int d = INF;
    for(i = 1; i <=fy; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
    {
    if(!visy[i] && d > slack[i])
    d = slack[i];
    }
    for(i = 1; i <=fx; i++)
    {
    if(visx[i])
    lx[i] -= d;
    }
    for(i = 1; i <=fy; i++)
    {
    if(visy[i])
    ly[i] += d;
    else
    slack[i] -= d;
    }
    }
    }
    int result = 0;
    for(i = 1; i <=fy; i++)
    if(linky[i]>-1)
    result += w[linky[i]][i];
    return result;
    }

    int main()
    {
    // freopen("g:/1.txt","r",stdin);
    int n,m;
    while(scanf("%d%d",&n,&m),n+m)
    {
    char cost[maxn];
    int i,j;fx=0,fy=0;
    for( i=0;i<n;i++)
    {
    scanf("%s",cost);
    for(int j=0;j<m;j++)
    if(cost[j]=='m')
    sx[++fx].x=i,sx[fx].y=j;
    else if(cost[j]=='H')
    sy[++fy].x=i,sy[fy].y=j;
    }
    for(i=1;i<=fx;i++)
    {
    for(j=1;j<=fy;j++)
    {
    int xx=fabs(sx[i].y-sy[j].y)+fabs(sx[i].x-sy[j].x);
    w[i][j]=-xx;
    }

    }

    printf("%d\n",-KM());
    }
    return 0;
    }

    第三题:  hdu  1853  Cyclic Tour

    注意是有向图,和重边的判断

    View Code
    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    int n,m;
    const int maxn = 105;
    const int INF = 0xffffff;

    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];

    bool find(int x)
    {
    visx[x] = true;
    for(int y = 1; y <=n; y++)
    {
    if(visy[y])
    continue;
    int t = lx[x] + ly[y] - w[x][y];
    if(t==0)
    {
    visy[y] = true;
    if(linky[y]==-1 || find(linky[y]))
    {
    linky[y] = x;
    return true; //找到增广轨
    }
    }
    else if(slack[y] > t)
    slack[y] = t;
    }
    return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }

    int KM() //返回最优匹配的值
    {
    int i,j;
    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i = 1; i <=n; i++)
    {
    lx[i] = -INF;
    for(j = 1; j <=n; j++)
    if(w[i][j] > lx[i])
    lx[i] = w[i][j];
    }

    for(int x = 1; x <=n; x++)
    {
    for(i = 1; i <=n; i++)
    slack[i] = INF;
    while(true)
    {
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    if(find(x)) //找到增广轨,退出
    break;
    int d = INF;
    for(i = 1; i <=n; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
    {
    if(!visy[i] && d > slack[i])
    d = slack[i];
    }
    for(i = 1; i <=n; i++)
    {
    if(visx[i])
    lx[i] -= d;
    }
    for(i = 1; i <=n; i++)
    {
    if(visy[i])
    ly[i] += d;
    else
    slack[i] -= d;
    }
    }
    }
    int result = 0 ;
    for(i = 1; i <=n; i++)
    {
    if(linky[i]==-1||w[linky[i]][i]==-INF)
    return 1;
    else result += w[linky[i]][i];
    }
    return result;
    }

    int main()
    {
    // freopen("g:/1.txt","r",stdin);

    while(scanf("%d%d",&n,&m)==2)
    {
    for(int i=1;i<=n;i++)
    {
    for(int j=1;j<=n;j++)
    w[i][j]=-INF;
    }
    for(int i=1;i<=m;i++)
    {
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    if(-c>w[a][b])
    w[a][b]=-c;
    }
    printf("%d\n",-KM());
    }
    return 0;
    }


    第四题:hdu  3488  Tour

    http://acm.hdu.edu.cn/showproblem.php?pid=3488
    跟第三题几乎一样

    View Code
    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    int n,m;
    const int maxn = 205;
    const int INF = 0xffffff;

    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];

    bool find(int x)
    {
    visx[x] = true;
    for(int y = 1; y <=n; y++)
    {
    if(visy[y])
    continue;
    int t = lx[x] + ly[y] - w[x][y];
    if(t==0)
    {
    visy[y] = true;
    if(linky[y]==-1 || find(linky[y]))
    {
    linky[y] = x;
    return true; //找到增广轨
    }
    }
    else if(slack[y] > t)
    slack[y] = t;
    }
    return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }

    int KM() //返回最优匹配的值
    {
    int i,j;
    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i = 1; i <=n; i++)
    {
    lx[i] = -INF;
    for(j = 1; j <=n; j++)
    if(w[i][j] > lx[i])
    lx[i] = w[i][j];
    }

    for(int x = 1; x <=n; x++)
    {
    for(i = 1; i <=n; i++)
    slack[i] = INF;
    while(true)
    {
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    if(find(x)) //找到增广轨,退出
    break;
    int d = INF;
    for(i = 1; i <=n; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
    {
    if(!visy[i] && d > slack[i])
    d = slack[i];
    }
    for(i = 1; i <=n; i++)
    {
    if(visx[i])
    lx[i] -= d;
    }
    for(i = 1; i <=n; i++)
    {
    if(visy[i])
    ly[i] += d;
    else
    slack[i] -= d;
    }
    }
    }
    int result = 0 ;
    for(i = 1; i <=n; i++)
    {
    if(linky[i]==-1||w[linky[i]][i]==-INF)
    return 1;
    else result += w[linky[i]][i];
    }
    return result;
    }

    int main()
    {
    // freopen("g:/1.txt","r",stdin);
    int cas; scanf("%d",&cas);
    while(cas--)
    {
    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;i++)
    {
    for(int j=1;j<=n;j++)
    w[i][j]=-INF;
    }
    for(int i=1;i<=m;i++)
    {
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    if(-c>w[a][b])
    w[a][b]=-c;
    }
    printf("%d\n",-KM());

    }

    return 0;
    }

    第五题:hdu  3435  A new Graph Game

    http://acm.hdu.edu.cn/showproblem.php?pid=3435
    跟第三题代码基本上一样,只是要双向建图,也有重边

    福大 AekdyCoin 出的题,好险的题啊,时间跑了 2000+ ;

    View Code
    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    int n,m;
    const int maxn = 1005;
    const int INF = 0xffffff;

    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];

    bool find(int x)
    {
    visx[x] = true;
    for(int y = 1; y <=n; y++)
    {
    if(visy[y])
    continue;
    int t = lx[x] + ly[y] - w[x][y];
    if(t==0)
    {
    visy[y] = true;
    if(linky[y]==-1 || find(linky[y]))
    {
    linky[y] = x;
    return true; //找到增广轨
    }
    }
    else if(slack[y] > t)
    slack[y] = t;
    }
    return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }

    int KM() //返回最优匹配的值
    {
    int i,j;
    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i = 1; i <=n; i++)
    {
    lx[i] = -INF;
    for(j = 1; j <=n; j++)
    if(w[i][j] > lx[i])
    lx[i] = w[i][j];
    }

    for(int x = 1; x <=n; x++)
    {
    for(i = 1; i <=n; i++)
    slack[i] = INF;
    while(true)
    {
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    if(find(x)) //找到增广轨,退出
    break;
    int d = INF;
    for(i = 1; i <=n; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
    {
    if(!visy[i] && d > slack[i])
    d = slack[i];
    }
    for(i = 1; i <=n; i++)
    {
    if(visx[i])
    lx[i] -= d;
    }
    for(i = 1; i <=n; i++)
    {
    if(visy[i])
    ly[i] += d;
    else
    slack[i] -= d;
    }
    }
    }

    }

    int main()
    {
    // freopen("g:/1.txt","r",stdin);
    int cas,p=1; scanf("%d",&cas);
    while(p<=cas)
    {
    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;i++)
    {
    for(int j=1;j<=n;j++)
    w[i][j]=-INF;
    }
    for(int i=1;i<=m;i++)
    {
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    if(-c>w[a][b])
    w[a][b]=w[b][a]=-c;
    }
    KM();
    int result = 0 ,flag=0;
    for(int i = 1; i <=n; i++)
    {
    if(linky[i]==-1||w[linky[i]][i]==-INF)
    {
    flag=1;break;
    }
    else result += w[linky[i]][i];
    }

    if(flag) printf("Case %d: NO\n",p++);
    else printf("Case %d: %d\n",p++,-result);

    }

    return 0;
    }

    第六题: hdu 2426  Interesting Housing Problem

    http://acm.hdu.edu.cn/showproblem.php?pid=2426

    注意 题目输入 |Vi| <= 10000 
    左集是学生,右集是房子,w[i][j] < 0 不可匹配,最后无法完美匹配输出-1

    View Code
    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    int n,m,v;
    const int maxn = 1005;
    const int INF = 0xffffff;

    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];

    bool find(int x)
    {
    visx[x] = true;
    for(int y = 0; y < m; y++)
    {
    if(visy[y])
    continue;
    int t = lx[x] + ly[y] - w[x][y];
    if(t==0)
    {
    visy[y] = true;
    if(linky[y]==-1 || find(linky[y]))
    {
    linky[y] = x;
    return true; //找到增广轨
    }
    }
    else if(slack[y] > t)
    slack[y] = t;
    }
    return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }

    int KM() //返回最优匹配的值
    {
    int i,j;
    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i = 0; i <n; i++)
    {
    lx[i] = -INF;
    for(j = 0; j <m; j++)
    if(w[i][j] > lx[i])
    lx[i] = w[i][j];
    }

    for(int x = 0; x <n; x++)
    {
    for(i = 0; i < m; i++)
    slack[i] = INF;
    while(true)
    {
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    if(find(x)) //找到增广轨,退出
    break;
    int d = INF;
    for(i = 0; i < m; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
    {
    if(!visy[i] && d > slack[i])
    d = slack[i];
    }
    for(i = 0; i < n; i++)
    {
    if(visx[i])
    lx[i] -= d;
    }
    for(i = 0 ; i < m; i++)
    {
    if(visy[i])
    ly[i] += d;
    else
    slack[i] -= d;
    }
    }
    }

    }

    int main()
    {
    // freopen("g:/1.txt","r",stdin);

    int p=1;
    while(scanf("%d%d%d",&n,&m,&v)==3)
    {
    for(int i=0;i<n;i++)
    {
    for(int j=0;j<m;j++)
    w[i][j]=-INF;
    }
    for(int i=0;i<v;i++)
    {
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    if(c < 0) continue ;
    w[a][b]=c;
    }
    if(v<=0) { printf("Case %d: -1\n",p++);continue;}
    KM();
    int result = 0 ,flag=0;
    for(int i = 0; i < m; i++)
    {
    if(linky[i]>-1&&w[linky[i]][i]!=INF)
    { result += w[linky[i]][i]; flag++;}
    }
    if(flag<n) result=-1;
    printf("Case %d: %d\n",p++,result);


    }

    return 0;
    }

    第七题:hdu 2853  Assignment

    http://acm.hdu.edu.cn/showproblem.php?pid=2853
    思路:让原有匹配更有优势就可以了
    实现:所有权值扩大100倍,原有匹配【例如a匹配b】w[a][b]+ +
    设结果是res
    最大值:res/100
    至少改变个数:n - res%100 

    这种处理比较有意思

    View Code
    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    int n,m,v;
    const int maxn = 1005;
    const int INF = 0xffffff;

    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];

    bool find(int x)
    {
    visx[x] = true;
    for(int y = 1; y <= m; y++)
    {
    if(visy[y])
    continue;
    int t = lx[x] + ly[y] - w[x][y];
    if(t==0)
    {
    visy[y] = true;
    if(linky[y]==-1 || find(linky[y]))
    {
    linky[y] = x;
    return true; //找到增广轨
    }
    }
    else if(slack[y] > t)
    slack[y] = t;
    }
    return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }

    void KM() //返回最优匹配的值
    {
    int i,j;
    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i = 1; i <= n; i++)
    {
    lx[i] = -INF;
    for(j = 1; j <=m; j++)
    if(w[i][j] > lx[i])
    lx[i] = w[i][j];
    }

    for(int x = 1; x <=n; x++)
    {
    for(i = 1; i <= m; i++)
    slack[i] = INF;
    while(true)
    {
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    if(find(x)) //找到增广轨,退出
    break;
    int d = INF;
    for(i = 1; i <= m; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
    {
    if(!visy[i] && d > slack[i])
    d = slack[i];
    }
    for(i = 1; i <= n; i++)
    {
    if(visx[i])
    lx[i] -= d;
    }
    for(i = 1 ; i <= m; i++)
    {
    if(visy[i])
    ly[i] += d;
    else
    slack[i] -= d;
    }
    }
    }

    }

    int main()
    {
    // freopen("g:/1.txt","r",stdin);


    while(scanf("%d%d",&n,&m)==2)
    {
    // memset(w,0,sizeof(w));
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
    int c;
    scanf("%d",&c);
    w[i][j]=c*100;
    }

    int num=0;
    for(int i=1;i<=n;i++)
    {
    int ss;
    scanf("%d",&ss);
    num+=w[i][ss];
    w[i][ss]++;
    }

    KM();
    int result = 0 ;
    for(int i = 1; i <= m; i++)
    {
    if(linky[i]!=-1)

    result += w[linky[i]][i];

    }
    printf("%d %d\n",n-result%100,result/100-num/100);

    }

    return 0;
    }

    第八题:hdu 3718 

    http://acm.hdu.edu.cn/showproblem.php?pid=3718
    题目求的是两字符串的最大相似度
    思路:因为第一个串的一种字母只能匹配第二个串的一种字母,所以可以转化为求
    【字母的最大匹配值/n】 注意输入 scanf--%s   不要用 %c%*c

    View Code
    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    int n,m,v;
    const int maxn = 30;
    const int INF = 0xffffff;

    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];

    bool find(int x)
    {
    visx[x] = true;
    for(int y = 1; y <27; y++)
    {
    if(visy[y])
    continue;
    int t = lx[x] + ly[y] - w[x][y];
    if(t==0)
    {
    visy[y] = true;
    if(linky[y]==-1 || find(linky[y]))
    {
    linky[y] = x;
    return true; //找到增广轨
    }
    }
    else if(slack[y] > t)
    slack[y] = t;
    }
    return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }

    void KM() //返回最优匹配的值
    {
    int i,j;
    memset(linky,-1,sizeof(linky));
    memset(ly,0,sizeof(ly));
    for(i = 1; i < 27; i++)
    {
    lx[i] = -INF;
    for(j = 1; j <27; j++)
    if(w[i][j] > lx[i])
    lx[i] = w[i][j];
    }

    for(int x = 1; x <27; x++)
    {
    for(i = 1; i < 27; i++)
    slack[i] = INF;
    while(true)
    {
    memset(visx,0,sizeof(visx));
    memset(visy,0,sizeof(visy));
    if(find(x)) //找到增广轨,退出
    break;
    int d = INF;
    for(i = 1; i < 27; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
    {
    if(!visy[i] && d > slack[i])
    d = slack[i];
    }
    for(i = 1; i < 27; i++)
    {
    if(visx[i])
    lx[i] -= d;
    }
    for(i = 1 ; i <27; i++)
    {
    if(visy[i])
    ly[i] += d;
    else
    slack[i] -= d;
    }
    }
    }

    }

    int main()
    {
    // freopen("g:/1.txt","r",stdin);
    char str[10005];
    int t;scanf("%d",&t);
    while(t--)
    {
    int v;
    scanf("%d%d%d%*c",&n,&m,&v);

    for(int i=0;i < n;i++)
    {
    char c[5];
    scanf("%s",c);
    str[i]=c[0];
    }

    for(int i=0;i< v;i++)
    {
    memset(w,0,sizeof(w));
    char ss[5];
    for(int j=0;j < n;j++)
    {
    scanf("%s",&ss);
    w[str[j]-'A'+1][ss[0]-'A'+1]++;
    }
    /* for(int kk=1;kk<=27;kk++)
    {
    for(int jj=1;jj<=27;jj++)
    printf("%d",w[kk][jj]);
    putchar('\n');
    }
    */
    KM();
    double result = 0 ;
    for(int i = 1; i < 27; i++)
    {
    if(linky[i]!=-1)

    result += w[linky[i]][i];

    }
    printf("%.4f\n",result/n);

    }

    }

    return 0;
    }

    推荐题目链接:

    http://972169909-qq-com.iteye.com/blog/1184514






     





    Just a little, maybe change the world
  • 相关阅读:
    ab访问并发测试简要步骤
    使用sysbench 进行msyql oltp压力测试
    pycham database查看db.sqlites文件 无内容解决方法
    mysql 批量修改字段方法
    mysql row日志格式下 查看binlog sql语句
    shiro自定义过滤器
    shiro【filter】
    js 中时间格式化的几种方法
    @Value("#{}")与@Value("${}")的区别
    IDEA 代码格式化,快捷键
  • 原文地址:https://www.cnblogs.com/skyming/p/2356919.html
Copyright © 2020-2023  润新知