解题思路:二分图。
建图:将学生的答案所映射的正确答案作为二分图的边,边权值为1,有两条就为2,依此类推。
然后用KM算法求出最佳匹配。除以n 就是要求的答案了。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define N 101
#define MIN -0xfffffff
#define MAX 0xfffffff
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(a) ((a)>0?(a):-(a))
const int alph = 26;
int map[N][N];
int l[N],r[N], ln[N],rn[N];
int link[N];
void init(){
memset(link,-1,sizeof(link));
memset(rn,0,sizeof(rn));
for(int i = 0; i < alph; i++){
ln[i] = MIN;
for(int j = 0; j < alph; j++){
ln[i] = max(ln[i],map[i][j]);
}
}
}
int dfs(int k) {
l[k] = 1;
for(int i = 0; i < alph; i++) {
if(!r[i]&&ln[k] + rn[i] == map[k][i]) {
r[i] = 1;
if(link[i] == -1 || dfs(link[i])) {
link[i] = k;
return 1;
}
}
}
return 0;
}
void adjust(){
int minm = MAX;
for(int i = 0; i < alph; i ++){
if(l[i]){
for(int j = 0; j < alph; j++){
if(!r[j]){
minm = min(minm,ln[i]+rn[j]-map[i][j]);
}
}
}
}
for(int i = 0; i < alph; i++){
if(l[i]){
ln[i] -= minm;
}
if(r[i]){
rn[i] += minm;
}
}
}
int main() {
int n, m, k;
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d%d",&n,&k,&m);
char ans[10001], s[11];
for(int i = 0; i < n; i++){
scanf("%s",s);
ans[i] = s[0];
}
while(m--){
memset(map,0,sizeof(map));
for(int i = 0; i < n; i++){
scanf("%s",s);
map[ans[i] - 'A'][s[0]-'A']++;
}
init();
for(int i = 0; i < alph; i++){
while(1){
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
if(dfs(i))break;
else adjust();
}
}
int sum = 0;
for(int i = 0; i < alph; i++){
sum += map[link[i]][i];
}
printf("%.4f\n",sum/(double)n);
}
}
return 0;
}