题意:
给定模板序列,和m个代考察序列,求出每个序列和模板序列的相似度。
其中相似度的定义:待考察序列中的相同字母,均可以替换成其他相同字母。例如 A B A A C 可以替换成 B B B B C 或者 C B C C C。或者A A A A A(B C都变成A)。替换完成后,对应位置字母相同的数量 ,除以序列长度即为答案。
解决:
每个待考察序列建图,扫一遍序列,图ma[26][26]对应字母替换之后的收益。即为二分图。跑一下KM。
#include <bits/stdc++.h> const int MAXN = 40; const int INF = 0x3f3f3f3f; int n, nx, ny; int lx[MAXN], ly[MAXN]; int ma[MAXN][MAXN]; int slack[MAXN], match[MAXN]; bool visx[MAXN], visy[MAXN]; bool findPath(int x) { visx[x] = true; for (int y = 1; y <= ny; ++y) { if (true == visy[y]) { continue; } int tmp = lx[x] +ly[y] - ma[x][y]; if (0 == tmp) { visy[y] = true; if (-1 == match[y] || true == findPath(match[y])) { match[y] = x; return true; } } if (slack[y] > tmp) slack[y] = tmp; } return false; } int km() { nx = ny = n; memset(match, -1, sizeof match); memset(ly, 0, sizeof ly); for (int x = 1; x <= nx; ++x) { lx[x] = -INF; for (int y = 1; y <= ny; ++y) { lx[x] = std::max(lx[x], ma[x][y]); } } for (int x = 1; x <= nx; ++x) { for (int y = 1; y <= ny; ++y) { slack[y] = INF; } while (true) { memset(visx, false, sizeof visx); memset(visy, false, sizeof visy); if (true == findPath(x)) break; int min_d = INF; for (int y = 1; y <= ny; ++y) { if (false == visy[y] && min_d > slack[y]) { min_d = slack[y]; } } for (int x = 1; x <= nx; ++ x) { if (true == visx[x]) { lx[x] -= min_d; } } for (int y = 1; y <= ny; ++y) { if (true == visy[y]) { ly[y] += min_d; } else slack[y] -= min_d; } } } int res = 0; for (int y = 1; y <= ny; ++y) { if (-1 != match[y]) { res += ma[match[y]][y]; } } return res; } char pat[10000+10]; char buf[10000+10]; int main() { int T; scanf("%d", &T); while (T--) { int l, tmp, m; scanf("%d%d%d", &l, &tmp, &m); n = 26; char cmd[4]; for (int i = 1; i <= l; ++i) { scanf("%s", cmd); pat[i] = cmd[0]; } while (m -- ){ memset(ma, 0, sizeof ma); for (int i = 1; i <= l; ++i) { scanf("%s", cmd); buf[i] = cmd[0]; ma[pat[i] - 'A' + 1][buf[i] - 'A' + 1]++; } int res = km(); // printf("res = %d ", res); printf("%.4f ", 1.0*res/l); } } }