丘比特的烦恼

题目描述 Description

  随着社会的不断发展,人与人之间的感情越来越功利化。最近,爱神丘比特发现,爱情也已不再是完全纯洁的了。这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭。于是丘比特千里迢迢远赴中国,找到了掌管东方人爱情的神——月下老人,向他求教。
  月下老人告诉丘比特,纯洁的爱情并不是不存在,而是他没有找到。在东方,人们讲究的是缘分。月下老人只要做一男一女两个泥人,在他们之间连上一条红线,那么它们所代表的人就会相爱——无论他们身处何地。而丘比特的爱情之箭只能射中两个距离相当近的人,选择的范围自然就小了很多,不能找到真正的有缘人。
  丘比特听了月下老人的解释,茅塞顿开,回去之后用了人间的最新科技改造了自己的弓箭,使得丘比特之箭的射程大大增加。这样,射中有缘人的机会也增加了不少。
  情人节(Valentine's day)的午夜零时,丘比特开始了自己的工作。他选择了一组数目相等的男女,感应到他们互相之间的缘分大小,并依此射出了神箭,使他们产生爱意。他希望能选择最好的方法,使被他选择的每一个人被射中一次,且每一对被射中的人之间的缘分的和最大。
  当然,无论丘比特怎么改造自己的弓箭,总还是存在缺陷的。首先,弓箭的射程尽管增大了,但毕竟还是有限的,不能像月下老人那样,做到“千里姻缘一线牵”。其次,无论怎么改造,箭的轨迹终归只能是一条直线,也就是说,如果两个人之间的连线段上有别人,那么莫不可向他们射出丘比特之箭,否则,按月下老人的话,就是“乱点鸳鸯谱”了。
  作为一个凡人,你的任务是运用先进的计算机为丘比特找到最佳的方案。

输入描述
Input Description

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

输出描述
Output Description

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

样例输入
Sample Input

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

样例输出
Sample Output

65

数据范围及提示
Data Size & Hint

n<30

p为小于等于255的正整数

思路:

就是建图跑二分图最优匹配,这里我用最小费用流求解。

几个需要注意的地方:

  1. 点以坐标形式给出,要运用一下计算机和的知识求出两点间距离、判断两点所连线段上是否有其他点;
  2. 点的特征值是一个字符串,且不区分大小写,需要注意;
  3. 题面保证没有重边但数据中有重边,且是取最后给出的边,这里比较坑。一是保证没有重边却出现重边debug了很久;二是想到可能存在重边但没想到竟不是取最好的边debug了更久(不过使用领接矩阵的话恰好跳过了这个坑点,大概标程写的KM吧);
  4. 数据中未给出的边存在默认权值为一的边,优先级低于距离限制和夹点限制。

代码:

#include<iostream>
#include<queue>
#include<cstdio>
#include<vector>
#include<map>
#include<cmath>
#include<cstring> using namespace std;
const int maxn = 2e4+5;
const int INF = 0x3f3f3f3f;
const double eps = 1e-4; struct Edge
{
int from, to, cap, flow, cost;
}; struct MCMF
{
int n, m, s, t;
vector<Edge> edges;
vector<int> G[maxn];
int inq[maxn];//是否在队列
int d[maxn];//Bellman-Ford用
int p[maxn];//上一条弧
int a[maxn];//可改进量 void init(int n)
{
this->n=n;
for(int i=0; i<n; ++i)
G[i].clear();
edges.clear();
} void AddEdge(int from, int to, int cap, int cost)
{
edges.push_back((Edge)
{
from, to, cap, 0, cost
});
edges.push_back((Edge)
{
to, from, 0, 0, -cost
});
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
} bool BellmanFord(int s, int t, int &flow, int& cost)
{
for(int i=0; i<n; ++i)
d[i]=INF;
memset(inq,0,sizeof(inq));
memset(p,0,sizeof(p));
d[s]=0;
inq[s]=1;
p[s]=0;
a[s]=INF; queue<int> Q;
Q.push(s);
while(!Q.empty())
{
int u=Q.front();
Q.pop();
inq[u]=0;
for(int i=0; i<G[u].size(); ++i)
{
Edge& e=edges[G[u][i]];
if(e.cap > e.flow && d[e.to] > d[u] + e.cost)
{
d[e.to]=d[u]+e.cost;
p[e.to]=G[u][i];
a[e.to]=min(a[u],e.cap-e.flow);
if(!inq[e.to])
{
Q.push(e.to);
inq[e.to]=1;
}
}
}
}
if(d[t]==INF)
return false;
flow+=a[t];
cost+=d[t]*a[t];
int u=t;
while(u!=s)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
u=edges[p[u]].from;
}
return true;
} int MinCost(int s, int t)
{
int flow=0, cost=0;
while(BellmanFord(s,t,flow,cost))
{
}
return cost;
}
} mcmf; class Person
{
public:
int x, y, num;
string name; Person() {} Person(int a, int b, int c, string str):x(a), y(b), num(c), name(str)
{
}
}; int k, n, s, t;
int grap[100][100];
map<string, int> msi;
vector<Person> persons; double Slope(Person& p1, Person& p2)
{
if(p1.x==p2.x)
return INF;
return 1.0*(p1.y-p2.y)/(p1.x-p2.x);
} int GetDis(Person& p1, Person& p2)
{
int maxx=p1.x, maxy=p1.y, minx=p2.x, miny=p2.y;
double sp=Slope(p1, p2);
if(maxx<minx) swap(maxx, minx);
if(maxy<miny) swap(maxy, miny);
for(int i=0; i<persons.size(); ++i)
{
if(i==p1.num || i==p2.num) continue;
Person& p3=persons[i];
if(p3.x<=maxx && p3.x>=minx && p3.y<=maxy && p3.y>=miny && abs(sp-Slope(p1, p3))<=eps)
return INF;
}
return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
} void trans(string& str)
{
for(int i=0; i<str.size(); ++i)
if(str[i]>='A' && str[i]<='Z')
str[i]+=32;
} void Init()
{
msi.clear();
persons.clear();
for(int i=0; i<100; ++i)
for(int j=0; j<100; ++j)
grap[i][j]=1;
cin>>k>>n;
k*=k;
int x, y, fate, dis;
string name, name2;
s=2*n;
t=s+1;
mcmf.init(t+1);
for(int i=0; i<2*n; ++i)
{
cin>>x>>y>>name;
trans(name);
persons.push_back(Person(x, y, i, name));
if(i<n)
mcmf.AddEdge(s, i, 1, 0);
else
mcmf.AddEdge(i, t, 1, 0);
msi[name]=i;
}
while(cin>>name)
{
if(name=="End")
break;
trans(name);
cin>>name2>>fate;
trans(name2);
int p1=msi[name], p2=msi[name2];
if(p1>p2) swap(p1, p2);
grap[p1][p2]=fate;
}
for(int i=0; i<n; ++i)
for(int j=n; j<2*n; ++j)
{
dis=GetDis(persons[i], persons[j]);
if(dis<=k)
mcmf.AddEdge(i, j, 1, -grap[i][j]);
}
} int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
Init();
cout<<-mcmf.MinCost(s, t)<<endl;
return 0;
}
 

bzoj2539 丘比特的烦恼、黑书P333 (最优二分图匹配)的更多相关文章

  1. BZOJ2539: [Ctsc2000]丘比特的烦恼

    BZOJ2539: [Ctsc2000]丘比特的烦恼 Description 随着社会的不断发展,人与人之间的感情越来越功利化. 最近,爱神丘比特发现,爱情也已不再是完全纯洁的了. 这使得丘比特很是苦 ...

  2. KM算法及其优化的学习笔记&&bzoj2539: [Ctsc2000]丘比特的烦恼

    感谢  http://www.cnblogs.com/vongang/archive/2012/04/28/2475731.html 这篇blog里提供了3个链接……基本上很明白地把KM算法是啥讲清楚 ...

  3. POI2001 Gold mine(二叉排序树 黑书经典)

    采矿(KOP) 金矿的老师傅年底要退休了.经理为了奖赏他的尽职尽责的工作,决定送他一块长方形地.长度为S,宽度为W.老师傅可以自己选择这块地.显然其中包含的采金点越多越好.你的任务就是计算最多能得到多 ...

  4. 图论(KM算法):COGS 290. [CTSC2008] 丘比特的烦恼

    290. [CTSC2008] 丘比特的烦恼 ★★★   输入文件:cupid.in   输出文件:cupid.out   简单对比 时间限制:1 s   内存限制:128 MB 随着社会的不断发展, ...

  5. 刘汝佳黑书 pku等oj题目

    原文地址:刘汝佳黑书 pku等oj题目[转]作者:小博博Mr 一.动态规划参考资料:刘汝佳<算法艺术与信息学竞赛><算法导论> 推荐题目:http://acm.pku.edu. ...

  6. cogs 290. [CTSC2000] 丘比特的烦恼

    290. [CTSC2000] 丘比特的烦恼 ★★★   输入文件:cupid.in   输出文件:cupid.out   简单对比时间限制:1 s   内存限制:128 MB 随着社会的不断发展,人 ...

  7. 【刷题】BZOJ 2539 [Ctsc2000]丘比特的烦恼

    Description 随着社会的不断发展,人与人之间的感情越来越功利化.最近,爱神丘比特发现,爱情也已不再是完全纯洁的了.这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭.于是 ...

  8. 1891: 丘比特的烦恼 - BZOJ

    Description 随着社会的不断发展,人与人之间的感情越来越功利化.最近,爱神丘比特发现,爱情也已不再是完全纯洁的了.这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭.于是 ...

  9. BZOJ 2539: [Ctsc2000]丘比特的烦恼

    Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 695  Solved: 260[Submit][Status][Discuss] Description ...

随机推荐

  1. 预科班D8

    2020.09.16星期三 预科班D8 学习内容: 一.注释 单行注释 ==>用 # 写在上一行或者该行后面 多行注释 ==>用 ''' '''或""" &q ...

  2. Java学习预热

    预备知识 什么是计算机 点击进入百度百科(计算机) 计算机硬件组成 主要部件 CPU 内存 主板 IO设备 键盘 鼠标 麦克风 显示器 音箱 冯·诺依曼体系结构 计算机软件 系统软件 DOS wind ...

  3. python身体指数BMI

    问题需求 既要输出国际标准也要输出国内标准

  4. 理解Java中的final关键字

    final关键字的基本用法 1. 修饰类 出于安全考虑,类无法被继承 2. 修饰方法 防止继承类修改方法private方法会隐式指定为final方法: 3. 修饰变量 基本数据类型,初始化后不能再修改 ...

  5. 在Notepad++中配置python运行环境

    1.当然首先还是需要先安装Python软件包的,推荐Python 3.X 2.打开Notepad++,[运行]--[运行],或者直接按F5快捷键,输入如下命令: cmd /k cd /d " ...

  6. Redis中的订阅模式

    redis中的客户端可以订阅一个自定义的频道,接受来自该频道的消息 订阅 订阅指定频道-SUBSCRIBE SUBSCRIBE channel [channel2]... SUBSCRIBE 频道名 ...

  7. Jackson反序列化枚举类型解决方案

    BUG描述 在使用SpringBoot自带的jackson处理客户端提交的消息体反序列化时,遇到一个非常棘手的情况 客户端发送如下json {"content":"654 ...

  8. Redis学习(五)Redis知识点总结

    一.基础概念 Q:什么是 Redis? 定义:Redis 是完全开源免费基于内存亦可持久化的,遵守 BSD 协议,是一个高性能的 key-value 数据库. 特点: 数据的持久化 :可以将内存中的数 ...

  9. 理解vue与MVVM三要素

    MVVM到底是什么,跟Jquery有什么区别? MVVM理解,跟MVC区别 Model View Controller,一般是用户操作view视图按钮,触发controller内方法,cotrolle ...

  10. Python-嵌套列表变成普通列表

    如何把[1, 5, 6, [2, 7, [3, [4, 5, 6]]]]变成[1, 5, 6, 2, 7, 3, 4, 5, 6]? 思考: -- for循环每次都遍历列表一层 -- 把取出的单个值加 ...