HDU-3718 Similarity 最大权值匹配

题意:给定一个进行了集合划分的序列,现有一套标准答案,同样给定了M个非标准答案,现在要计算这M个非标准答案的最大正确率为多少?

解法:该题考虑的是可以采用不同的字符进行相同的划分。那么求解的就是一个集合符号的匹配的问题,采用何种的集合符号一一对应才能使得正确率最高。那么对于每对应某个位置的集合标号,我们可以假设是对应的,那么在这个基础上再进行更多的匹配,建立起x字符对应y字符最多能够对多少个的一个边,然后调用KM算法即可。在读取数据的时候使用gechar死活过不了(会读到非字母导致RE,可能是数据中有多个空格或者...),还好有cin这个利器,不过就是速度慢了点。

代码如下:

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int N, M, K;
int w[30][30];
int lx[30], ly[30];
int sx[30], sy[30];
int match[30], slack[30];
char ans[10005];
char tmp[10005];
vector<int>v[30];

void build() { // 构图
    memset(w, 0, sizeof (w));
    int cnt, lch, rch;
    for (int i = 1; i <= N; ++i) {
        cnt = 0;
        lch = tmp[i]-'A', rch = ans[i]-'A';if (!w[lch][rch]) {
            for (int j = 0; j < v[rch].size(); ++j) {
                if (tmp[v[rch][j]] == tmp[i]) {
                    ++cnt;
                }
            }
            w[lch][rch] = cnt;
        }
    }
}

int path(int u) {
    sx[u] = 1;
    for (int i = 0; i < 26; ++i) {
        if (sy[i]) continue;
        int t = lx[u] + ly[i] - w[u][i];
        if (!t) {
            sy[i] = 1;
            if (match[i] == -1 || path(match[i])) {
                match[i] = u;
                return true;
            }    
        } else {
            slack[i] = min(slack[i], t);
        }
    }
    return false;
}

void KM() {
    memset(match, 0xff, sizeof (match)); // 由于0号元素同样是有效编号,初始化为-1
    memset(lx, 0, sizeof (lx));
    memset(ly, 0, sizeof (ly));
    for (int i = 0; i < 26; ++i) {
        for (int j = 0; j < 26; ++j) {
            lx[i] = max(lx[i], w[i][j]);    
        }
    }
    for (int i = 0; i < 26; ++i) {
        memset(slack, 0x3f, sizeof (slack));
        while (1) {
            memset(sx, 0, sizeof (sx));
            memset(sy, 0, sizeof (sy));
            if (path(i)) break;
            int d = 0x3f3f3f3f;
            for (int j = 0; j < 26; ++j) {
                if (!sy[j])    d = min(d, slack[j]);
            }
            for (int j = 0; j < 26; ++j) {
                if (sx[j]) lx[j] -= d;
                if (sy[j]) ly[j] += d;
                else slack[j] -= d;
            }
        }
    }
    int ret = 0;
    for (int i = 0; i < 26; ++i) {
        ret += w[match[i]][i];    
    }
    printf("%.4f\n", 1.0*ret/N);
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        for (int i = 0; i < 26; ++i) {
            v[i].clear();
        }
        scanf("%d %d %d", &N, &K, &M);
        char ch;
        for (int i = 1; i <= N; ++i) {
            cin >> ans[i];
            v[ans[i]-'A'].push_back(i); // 把为某一字符的位置统一保存起来
        }
        for (int i = 0; i < M; ++i) {
            for (int j = 1; j <= N; ++j) {
                cin >> tmp[j];
            }
            build();
            KM();
        }
    }    
    return 0;    
}

 

posted @ 2013-04-17 17:59  沐阳  阅读(410)  评论(0编辑  收藏  举报