【最大费用流】【最优匹配】丘比特的烦恼 Vijos 1169

该博客讨论了Vijos 1169题目的解决过程,涉及最大费用流和最优匹配。丘比特试图在有限射程内寻找缘分最大化的男女匹配,博客介绍了输入输出格式,并分享了因数据格式和规则理解错误导致的困扰,如重边处理、射程限制和缘分默认值等问题,强调了正确理解题目细节的重要性。

Vijos-P1169丘比特的烦恼

描述

随着社会的不断发展,人与人之间的感情越来越功利化。最近,爱神丘比特发现,爱情也已不再是完全纯洁的了。这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭。于是丘比特千里迢迢远赴中国,找到了掌管东方人爱情的神——月下老人,向他求教。

月下老人告诉丘比特,纯洁的爱情并不是不存在,而是他没有找到。在东方,人们讲究的是缘分。月下老人只要做一男一女两个泥人,在他们之间连上一条红线,那么它们所代表的人就会相爱——无论他们身处何地。而丘比特的爱情之箭只能射中两个距离相当近的人,选择的范围自然就小了很多,不能找到真正的有缘人。

丘比特听了月下老人的解释,茅塞顿开,回去之后用了人间的最新科技改造了自己的弓箭,使得丘比特之箭的射程大大增加。这样,射中有缘人的机会也增加了不少。

情人节(Valentine's day)的午夜零时,丘比特开始了自己的工作。他选择了一组数目相等的男女,感应到他们互相之间的缘分大小,并依此射出了神箭,使他们产生爱意。他希望能选择最好的方法,使被他选择的每一个人被射中一次,且每一对被射中的人之间的缘分的和最大。

当然,无论丘比特怎么改造自己的弓箭,总还是存在缺陷的。首先,弓箭的射程尽管增大了,但毕竟还是有限的,不能像月下老人那样,做到“千里姻缘一线牵”。其次,无论怎么改造,箭的轨迹终归只能是一条直线,也就是说,如果两个人之间的连线段上有别人,那么莫不可向他们射出丘比特之箭,否则,按月下老人的话,就是“乱点鸳鸯谱”了。

作为一个凡人,你的任务是运用先进的计算机为丘比特找到最佳的方案。

格式

输入格式

输入第一行为正整数k,表示丘比特之箭的射程,第二行为正整数n(n<30),随后有2n行,表示丘比特选中的人的信息,其中前n行为男子,后n行为女子。每个人的信息由两部分组成:他的姓名和他的位置。姓名是长度小于20且仅包含字母的字符串,忽略大小写的区别,位置是由一对整数表示的坐标,它们之间用空格分隔。格式为x y Name。输入文件剩下的部分描述了这些人的缘分。每一行的格式为Name1 Name2 p。Name1和Name2为有缘人的姓名,p是他们之间的缘分值(p为小于等于255的正整数)。以一个End作为文件结束标志。每两个人之间的缘分至多只被描述一次。如果没有被描述,则说明他们缘分值为1。

输出格式

输出仅一个正整数,表示每一对被射中的人之间的缘分的总和。这个和应当是最大的。

样例1

样例输入1

2
3
0 0 Adam
1 1 Jack
0 2 George
1 0 Victoria
0 1 Susan
1 2 Cathy
Adam Cathy 100
Susan George 20
George Cathy 40
Jack Susan 5
Cathy Jack 30
Victoria Jack 20
Adam Victoria 15
End

样例输出1

65



不想吐槽了。。。。。

首先是题目描述,样例明明是先读入坐标(x,y)在读入名字,但是题目描述是先名字后坐标。。。。纠结了好久。。。。

抱着试一试的心态才知道是先坐标后名字

题目说的没有重边,但是数据有!

得知数据有重边后我又判断,取最优的那条边,但是数据是取的最后出现的那条边!


还有,几何不好的孩子太苦逼了。。。。。。


下面是我们需要注意的几点

1.弓箭的射程有限。 
2.箭的轨迹只能是一条直线,两个人之间的连线段不能有别人。 
3.忽略大小写的区别。 
4.如果没有被描述,则说明他们缘分值为1,但并不一定有边!(即在射程内)
6.重边取最后一条


关键是第二点。判了好久啊。。。。。。。。。。。。。。。。。。。。。

做了一上午,都在判第二点。。。。。。


测评情况(Vijos):



C++ AC Code

/*http://blog.csdn.net/jiangzh7
By Jiangzh*/
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int N=1000;
const int inf=0x3f3f3f3f;
#define sqr(_) ((_)*(_))
typedef long long LL;
int K,n;
struct node{char name[50];int x,y;}p[N];
int cap[N][N],cost[N][N];
int S,T;
int dist[N],h[N],pre[N];
queue<int> q;

void predoing(char *s)//全部转化为小写
{
	int len=strlen(s);
	for(int i=0;i<len;i++) s[i]=tolower(s[i]);
}

void read()
{
	scanf("%d%d\n",&K,&n);
	for(int i=1;i<=n*2;i++)
	{
		scanf("%d%d%s\n",&p[i].x,&p[i].y,p[i].name);
		predoing(p[i].name);
	}
}

int find(char *s)//找到对应名字的序号
{
	for(int i=1;i<=n*2;i++)
	if(!strcmp(s,p[i].name)) return i;
}

double dis(int x1,int y1,int x2,int y2)//距离
{
	return sqrt((double)sqr((LL)x1-x2)+(double)sqr((LL)y1-y2));
}

bool check(int i,int j)//检查能否连边
{
	int x1=p[i].x,y1=p[i].y;
	int x2=p[j].x,y2=p[j].y;
	if(dis(x1,y1,x2,y2)>(double)K) return false;
	for(int i=1;i<=n+n;i++)
	{
		if(p[i].x==x1&&p[i].y==y1) continue;
		if(p[i].x==x2&&p[i].y==y2) continue;
		int x0=p[i].x,y0=p[i].y;
		if((x0-x1)*(x0-x2)<=0 && (y0-y1)*(y0-y2)<=0)
		if((x0-x1)*(y2-y1)==(y0-y1)*(x2-x1))//p在boy和girl连线上
			return false;
	}
	return true;
}

void build_map()//建图
{
	char b[50],g[50];
	for(int i=1;i<=n;i++)
	for(int j=n+1;j<=n+n;j++)
	if(dis(p[i].x,p[i].y,p[j].x,p[j].y)<=K)//必须在射程内
	{
		cap[i][j]=1;
		cost[i][j]=1;
		cost[j][i]=-1;
	}
	int x,y,w;
	while(1)
	{
		memset(b,0,sizeof(b));
		memset(g,0,sizeof(g));
		scanf("%s",b);if(!strcmp(b,"End")) break;
		scanf("%s%d\n",g,&w);
		predoing(b);predoing(g);
		x=find(b);y=find(g);
		if(x>y) swap(x,y);//不一定是男在前,所以必要的时候交换一下位置
		if(check(x,y))
		{
			cost[x][y]=w;
			cost[y][x]=-w;
		}
	}
	S=0;T=n+n+1;//源点和汇点
	for(int i=1;i<=n;i++) {cap[S][i]=1;cost[S][i]=0;}
	for(int i=n+1;i<=n+n;i++) {cap[i][T]=1;cost[i][T]=0;}
}

bool spfa()
{
	for(int i=S;i<=T;i++) dist[i]=-inf;
	dist[S]=0;q.push(S);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();h[x]=false;
		for(int i=S;i<=T;i++)
		if(cap[x][i]>0 && dist[i]<dist[x]+cost[x][i])
		{
			pre[i]=x;
			dist[i]=dist[x]+cost[x][i];
			if(!h[i])
			{
				h[i]=true;
				q.push(i);
			}
		}
	}
	return dist[T]!=-inf;
}

void work()
{
	build_map();
	int maxcost=0;
	while(spfa())
	{
		int res=inf;
		for(int i=T;i!=S;i=pre[i])
		{
			res=min(res,cap[pre[i]][i]);
		}
		for(int i=T;i!=S;i=pre[i])
		{
			cap[pre[i]][i]-=res;
			cap[i][pre[i]]+=res;
		}
		maxcost+=res*dist[T];
	}
	printf("%d\n",maxcost);
}

int main()
{
	freopen("cupid.in","r",stdin);
	freopen("cupid.out","w",stdout);
	read();
	work();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值