hdu3718Similarity KM算法

//n个物品,用字母表示每个物品的种类,
//给出标准答案对于每种物品的字母表示
//给出每个人的答案,问怎样的匹配使得两个答案的相似度更高
//{A A B A B B C C C C},{F F E F E E D D D D}表示一种答案
//对每个字母和字母之间建图,两个答案在同一个位置的字母之间的边的权值加1
//那么就只需要求其最大带权匹配
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std ;
const int maxn = 10100 ;
const int inf = 0x3f3f3f3f ;
int w[30][30] ;
int idx[30] , idy[30] ;
int match[30] ;
char str[maxn][10] ;   int n , m , k;
int lx[30] , ly[30] ;
int visx[30] , visy[30] , slack[30] ;

bool find(int x)
{
    visx[x] = 1 ;
    for(int i = 1;i <= k;i++)
    {
        if(visy[i]) continue ;
        int tmp = lx[x] + ly[i] - w[x][i] ;
        if(tmp == 0)
        {
            visy[i] = 1 ;
            if(match[i] == -1 || find(match[i]))
            {
               match[i] = x;
              return true ;
             }
        }
        else slack[i] = min(slack[i] , tmp) ;
    }
    return false ;
}
int KM()
{
    memset(match , -1 , sizeof(match)) ;
    memset(ly , 0 ,sizeof(ly)) ;
    for(int i = 1;i <= k;i++)
    {
        lx[i] = -inf ;
        for(int j = 1;j <= k;j++)
        lx[i] = max(lx[i] , w[i][j]) ;
    }
    for(int i = 1;i <= k;i++)
    {
        for(int j = 1;j <= k;j++)
        slack[j] = inf ;
        while(1)
        {
            memset(visx , 0 , sizeof(visx)) ;
            memset(visy , 0 , sizeof(visy)) ;
            if(find(i))break;
            int d = inf ;
            for(int j = 1 ;j <= k;j++)
            if(!visy[j])
            d = min(d , slack[j]) ;
            for(int j = 1;j <= k;j++)
            if(visx[j])
              lx[j] -= d ;
            for(int j = 1 ;j <= k;j++)
            if(visy[j])
              ly[j] += d ;
            else
             slack[j] -= d ;
        }
    }
    int ans = 0 ;
    for(int i  =1;i <= k;i++)
    ans += w[match[i]][i] ;
    return ans ;
}
int main()
{
   // freopen("in.txt" ,"r" , stdin) ;
    int T ;
    scanf("%d" ,&T) ;
    while(T--)
    {
        scanf("%d%d%d" , &n , &k , &m) ;
        memset(idx , 0 , sizeof(idx)) ;
        int len = 0 ;
        for(int i = 1;i <= n;i++)
        {
            scanf("%s"  ,str[i]);
            if(!idx[str[i][0]-'A'])
            idx[str[i][0]-'A'] = ++len ;
        }
        while(m--)
        {
            char ch[3] ;
            memset(w , 0 , sizeof(w)) ;
            memset(idy , 0 ,sizeof(idy)) ;
            len = 0 ;
            for(int i = 1;i <= n;i++)
            {
                scanf("%s" , ch) ;
                if(!idy[ch[0]-'A'])idy[ch[0]-'A'] = ++len ;
                w[idx[str[i][0]-'A']][idy[ch[0]-'A']]++ ;
            }
            int ans = KM() ;
            printf("%.4lf\n" , (double)ans/(double)n) ;
        }
    }
    return 0 ;
}























































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值