zoukankan      html  css  js  c++  java
  • scau 8616 汽车拉力比赛

          上次我们过了二分图的最佳匹配,现在我们看一道题目,经典的二分图的最佳匹配题目

    8616 汽车拉力比赛

    时间限制:500MS  内存限制:1000K
    提交次数:71 通过次数:24

    题型: 编程题   语言: G++;GCC

     

    Description

     SCAU车队要去参加汽车拉力比赛啦。拉力比赛中,每辆汽车需要一个驾驶员和一个导航员。SCAU车队一共有有N个驾驶员和N个导航员,每个 驾驶员对每个导航员有一个默契值,现在需要求得某种N对N的配对,使得这N个配对的默契值之和最大。 

    输入格式

     多Case,每一个Case第一行是一个整数N,范围是(1<=N<=16)。接下来有N行,每行有N个数,第i行第j个数表示第i个驾驶员和第j个导航员的默契值。 测试数据会有多组,当N=0时表示数据结束,这行不用处理。

    输出格式

     输出配对后默契值之和的最大值。

    输入样例

    1
    5
    2
    10 20
    25 5
    0
    

    输出样例

    5
    45
    至于如何做,我就不说了,看看我以前的博客吧,思路应该很清晰的
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define inf 99999
    //保存一个我们认为的无穷大
    int n;
    int match[50];//表示这个[驾驶员]搭配那个驾驶员
    int e[50][50];//用来保存一张图,表示i-->j的默契值
    int vx[50];//用来表示 驾驶员 来找过partner
    int vy[50];//用来表示 导航员 被邀请过搭配
    int fx[50];//一开始是用来表示每一个驾驶员与1--n个导航员的max,但是随着程序执行,会变化哦
    int fy[50];//一开始是0的。我们称这两个东西为 标干
    int dfs (int u)//我要来找u的partner
    {
        vx[u]=1;//u已经来找过partner啦,标记一下先,待会的km算法需要
        int i;
        for (i=1;i<=n;i++)//筛选n个 导航员  可以知道,驾驶员比较“主动”
        {
            if (vy[i]==0&&fx[u]+fy[i]==e[u][i])//这个导航员没有被邀请过..导航员为u
            //驾驶员和导航员的【标杠】值相加,有这个默契值的话,这样选出来的是最优的
            {
                //vy[i]==1;//这个导航员被邀请过了,但是,接不接受?还是另外一回事
                //不是等于等于
                //因为他还可能是被别人搭配了嘛
                vy[i]=1;
                if (match[i]==0||dfs(match[i]))//如果他没被人搭配了或者,它可以找其他人搭配
                {
                    match[i]=u;//i搭配u  改变条件的
                    //意思就是导航员i和驾驶员u搭配
                    return 1;//搭配成功
                }
            }
        }
        return 0;//我找不到啊,后面,就会执行km
    }
    void do_km()//km算法
    //对每一个在vx[]数组中的驾驶员,我们对他进行操作
    //操作对象是每一个不在vy[]数组中的导航员,在了的导航员是不能动的,是当前的最优解
    {
        int i,j;
        int d;//表示默契值减小得最多,就是,搭配最大
        d=inf;//认为他是无限大
        for (i=1;i<=n;i++)//遍历每一个驾驶员
        {
            if (vx[i]==1)//表示他是遍历过的
            {
                for (j=1;j<=n;j++)//对他进行遍历导航员
                {
                    if (!vy[j])//没有vy过的导航员才操作
                    {
                       if (d>(fx[i]+fy[j]-e[i][j]))//对于后面没vy的导航员,选出最小差值
                       {
                           d=fx[i]+fy[j]-e[i][j];
                           //printf ("%d
    ",d);
                       }
                    }
                }
            }
        }
        //对于每一个vx[]的 fx[] 减去d
        //对于每一个vy[]的 fy[] 加上d
        //这样,总值会减小哦,就是,我们有机会加入新的导航员,留下空位
        for (i=1;i<=n;i++)
        {
            if (vx[i]==1)
            {
                fx[i] -= d;
                vx[i]=0;//请0
            }
            if (vy[i]==1)//是访问过的哦
            {
                fy[i] += d;
                vy[i]=0;//情0
            }
        }
        return ;
    }
    void work ()
    {
        int i,j;
        memset (e,0,sizeof(e));
        memset (vx,0,sizeof(vx));
        memset (vy,0,sizeof(vy));
        memset (fx,0,sizeof(fx));
        memset (fy,0,sizeof(fy));
        memset (match,0,sizeof(match));
    
        for (i=1;i<=n;i++)
        {
            for (j=1;j<=n;j++)
            {
                scanf ("%d",&e[i][j]);//不是无向图哦,有向的
                //就是e[i][j]!=e[j][i]  有可能相等
            }
        }
        //km算法的一部分,先初始化fx,fy
        for (i=1;i<=n;i++)//遍历每一个驾驶员
        {
            fy[i]=0;
            fx[i]= -inf;//无穷小
            for (j=1;j<=n;j++)//遍历每一个导航员
            {
                 if (fx[i]<e[i][j])//默契值
                 {
                     fx[i]=e[i][j];
                 }
            }
        }
        for (i=1;i<=n;i++)//遍历每一个驾驶员
        {
            memset (vx,0,sizeof(vx));//每一个驾驶员都是新的开始
            memset (vy,0,sizeof(vy));//要清零,
            while (!dfs(i))//如果他找不到搭配,就实现km算法
            {
                do_km();//km完后,还是会对这个想插入的节点进行dfs的,因为他还没搭配成功嘛
            }
        }
        //进行计数
        //现在match[i]记录的是[导航员i]和[驾驶员match[i]]搭配
        int ans=0;
        for (i=1;i<=n;i++)//遍历导航员
        {
            ans += e[match[i]][i];//输入的第一个是驾驶员,第二个是导航员
        }
        printf ("%d
    ",ans);
        return ;
    }
    int main ()
    {
        while (scanf ("%d",&n)!=EOF && n)
        {
            work ();
        }
        return 0;
    }
    View Code
     
  • 相关阅读:
    什么是Web Service?
    按钮上显示值的轮流切换
    跟偶一起做:击退眼睛疲劳的五大运动
    Windows下权限设置详解
    实现数据分类汇总的SQL语句
    毕业不吼不快十首经典歌曲
    使用命令查看自己的外网IP地址
    如何面对30岁?
    JavaScript中this关键字使用方法详解
    发掘WinRAR的“自解压安装”功能
  • 原文地址:https://www.cnblogs.com/liuweimingcprogram/p/5147966.html
Copyright © 2011-2022 走看看