hdu2813解题报告

题意:吕布带着n个士兵和曹操的m个士兵打仗,有k场战争,每场有 string1  string2   J   三个数据,代表吕布的string1这个人和曹操的string2这个人打会受到 J 的伤害,问:如何选择使得吕布的n个士兵都参加,并且获得最小伤害,求最小伤害

分析:这里很明确是KM的最小完备匹配

但是,自己在这里有个误区:作为刚接触KM不久的我来说,还真的是对KM不太熟悉,我总会认为,在匹配的过程中,m>n的情况下,可能有一种情况,就是有的m的点,在匹配的时候,曾匹配过n中的点,但是在后面的更优的匹配过程中被挤开匹配其他点,那么多次匹配的过程之后,是不是会有link[j] 这个j 最后没有被匹配,但是中途被匹配过一次,link[j] 不为 -1,那么在计算最后的 ans 的值得时候那不就多算了吗?

这里为自己解释这个误区:其实在匹配的过程中,我们有集合v1 ,v2 和边集 E,在对V1的每个点进行匹配时,我们在E中对于V1 的 i 和 V2 的 j 有关系,就会匹配 link[j] = i;  那么之后如果对于V1中的又一个点,可以匹配V2的 j 的时候,我们会尝试 挤开 匹配了V2的j的 i 这个点,让它尝试匹配别的点,如果匹配到,那么那一个V2的点的link就是 i ,而回溯回来 j 的link 就更新为当前匹配的v1的点,所以,在整个匹配过程中,不会有中间匹配过,而最后被空闲的点

那么回到hdu2813,直接上马(注意中间的人名就用map 处理,时间用的比较多)

//765MS 492K 
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
using namespace std;

#define MAX 3
#define INF 1<<30-1

int n,m,k;
int w[MAX][MAX];
int lx[MAX],ly[MAX],visx[MAX],visy[MAX],slack[MAX],link[MAX];

bool dfs(int x)
{
	visx[x] = 1;
	for(int y = 0; y < m; y ++)
	{
		if(visy[y]) continue;
		int t = lx[x] + ly[y] - w[x][y];
		if(t == 0)
		{
			visy[y] = 1;
			if(link[y] == -1 || dfs(link[y]))
			{
				link[y] = x; return true;
			}
		}
		else if(slack[y] > t)
			slack[y] = t;
	}
	return false;
}

int KM()
{
	memset(link,-1,sizeof(link));
	memset(ly,0,sizeof(ly));
	for(int i = 0; i < n; i ++)
	{
		lx[i] = -INF;
		for(int j = 0 ; j < m; j ++)
			if(lx[i] < w[i][j])
				lx[i] = w[i][j];
	}
	for(int x = 0; x < n; x ++)
	{
		for(int k = 0; k < m; k ++) slack[k] = INF;
		while(1)
		{
			memset(visx,0,sizeof(visx));
			memset(visy,0,sizeof(visy));
			if(dfs(x)) break;

			int d = INF;
			for(int i = 0; i < m; i ++)
				if(!visy[i] && slack[i] < d) d = slack[i];
			for(int j = 0; j < m; j ++)
			{
				if(visx[j]) lx[j] -= d;
				if(visy[j]) ly[j] += d;
				else        slack[j] -= d;
			}
		}
	}
	int ans = 0;
	for(int k = 0; k < m; k ++)
	{
		if(link[k] != -1)
			ans += w[link[k]][k];
	}
	return ans;
}

int main()
{
	string str1,str2;
	int t;//人名和伤害值
	while(~scanf("%d%d%d",&n,&m,&k))
	{
		for(int i = 0; i < n; i ++)
		{
			for(int j = 0; j < m ; j ++)
				w[i][j] = -INF;
		}
		map<string,int>m1,m2;
		map<string,int>::iterator it;
		int sum1 = 0,sum2 = 0;//对吕布。曹操人的编号,从0开始
		while(k --)
		{
			int a,b;
			cin>>str1>>str2>>t;
			it = m1.find(str1);
			if(it == m1.end())
			{
				a = sum1++;
				m1[str1] = a;
			}
			else
				a = (*it).second;
			it = m2.find(str2);
			if(it == m2.end())
			{
				b = sum2++;
				m2[str2] = b;
			}
			else
				b = (*it).second;

			w[a][b] = -t; //
		}
		printf("%d\n",-KM());
	}
	return 0;
}
个人愚昧观点,欢迎指正与讨论

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值