HDU 3718 Similarity(二分图最大匹配问题)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3718

题目意思:有两串字符串,他们都是分类的结果.第一行是正确结果,第二行是需要你判断的.比如:

       A A B A B B C C C C

       S T R S T R S T  R S

       当S对应A,T对应B,R对应C时, 它们匹配的位置个数=4

       当S对应C,T对应A,B对应R时, 它们匹配的位置个数=5

       明显第二种匹配方式更好,字符串相似率达到了5/10=50%.

       现在给你很多字符串,要你输出最大相似率.

这题难在了建图上,参考了大佬的题解https://blog.csdn.net/discreeter/article/details/50331785,可以以每对对应字母建立边关系,遍历学生的答案和正确答案,得到边的权值,然后利用二分图的KM算法,套模板,得出最大匹配权值,最后输出最大匹配权值/字符串长度 即相似率.

关于KM算法参考这两个大佬的博客:1 http://www.cnblogs.com/wenruo/p/5264235.html     2   https://www.cnblogs.com/logosG/p/logos.html

贴上AC代码:

// ProblemN.cpp: 定义控制台应用程序的入口点。
//

//#include "stdafx.h"
#include<iostream>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;
const int INF = 0x3f3f3f3f;
char s1[10010], s2[10010];
int line[60][60], link[60], slack[60], fx[60], fy[60];
bool visx[60], visy[60];

int n, m, k;

bool Find(int x)
{
	visx[x] = true;
	for (int i = 0; i < 26; i++)
	{
		if (visy[i])continue;
		int gap = fx[x] + fy[i] - line[x][i];
		if (gap == 0)
		{
			visy[i] = true;
			if (link[i] == -1 || Find(link[i]))
			{
				link[i] = x;
				return true;
			}
			
		}
		else
			{
				slack[i] = min(slack[i], gap);
			}
	}
	return false;
}

int KM()
{
	memset(link, -1, sizeof(link));
	memset(fy, 0, sizeof(fy));
	for (int i = 0; i < 26; i++)
	{
		fx[i] = line[i][0];
		for (int j = 1; j < 26; j++)
		{
			fx[i] = max(fx[i], line[i][j]);
		}
	}

	for (int i = 0; i < 26; i++)
	{
		fill(slack, slack + 26, INF);
		while (true)
		{
			memset(visx, 0, sizeof(visx));
			memset(visy, 0, sizeof(visy));
			if (Find(i))break;
			int d = INF;
			for (int j = 0; j < 26; j++)
			{
				if (!visy[j])
					d = min(d, slack[j]);
			}
			for (int j = 0; j < 26; j++)
			{
				if (visx[j])
					fx[j] -= d;
				if (visy[j])
					fy[j] += d;
				else
				{
					slack[j] -= d;
				}
			}
		}
	}
	int res = 0;
	for (int i = 0; i < 26; i++)
		res += line[link[i]][i];
	return res;
}
int main()
{
	int t;
	while (cin >> t)
	{
		while (t--)
		{
			cin >> n >> k >> m;
			for (int i = 0; i < n; i++)
				cin >> s1[i];
			for (int i = 0; i<m; i++)
			{
				for (int j = 0; j < n; j++)
					cin >> s2[j];
				memset(line, 0, sizeof(line));
				for (int j = 0; j < n; j++)
					line[s2[j] - 'A'][s1[j] - 'A']++;//建图,遍历两个字符串,得到权值
				double ans = 1.0*KM() / n;
				cout << fixed << setprecision(4) << ans << endl;
			}
		}
	}
	return 0;
}

                                                                           

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值