标签: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 ;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:km算法
原文地址:http://blog.csdn.net/cq_pf/article/details/47342717