★☆【二分圖最佳匹配】丘比特的煩惱
来源:互联网 发布:6月淘客不计入淘宝权重 编辑:程序博客网 时间:2024/06/10 16:29
随着社会的不断发展,人与人之间的感情越来越功利化。最近,爱神丘比特发现,爱情也已不再是完全纯洁的了。这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭。于是丘比特千里迢迢远赴中国,找到了掌管东方人爱情的神——月下老人,向他求教。 月下老人告诉丘比特,纯洁的爱情并不是不存在,而是他没有找到。在东方,人们讲究的是缘分。月下老人只要做一男一女两个泥人,在他们之间连上一条红线,那么它们所代表的人就会相爱——无论他们身处何地。而丘比特的爱情之箭只能射中两个距离相当近的人,选择的范围自然就小了很多,不能找到真正的有缘人。 丘比特听了月下老人的解释,茅塞顿开,回去之后用了人间的最新科技改造了自己的弓箭,使得丘比特之箭的射程大大增加。这样,射中有缘人的机会也增加了不少。 情人节(Valentine's day)的午夜零时,丘比特开始了自己的工作。他选择了一组数目相等的男女,感应到他们互相之间的缘分大小,并依此射出了神箭,使他们产生爱意。他希望能选择最好的方法,使被他选择的每一个人被射中一次,且每一对被射中的人之间的缘分的和最大。 当然,无论丘比特怎么改造自己的弓箭,总还是存在缺陷的。首先,弓箭的射程尽管增大了,但毕竟还是有限的,不能像月下老人那样,做到“千里姻缘一线牵”。其次,无论怎么改造,箭的轨迹终归只能是一条直线,也就是说,如果两个人之间的连线段上有别人,那么莫不可向他们射出丘比特之箭,否则,按月下老人的话,就是“乱点鸳鸯谱”了。 作为一个凡人,你的任务是运用先进的计算机为丘比特找到最佳的方案。输入文件格式: 输入文件第一行为正整数k,表示丘比特之箭的射程,第二行为正整数n(n<30),随后有2n行,表示丘比特选中的人的信息,其中前n行为男子,后n行为女子。每个人的信息由两部分组成:他的姓名和他的位置。姓名是长度小于20且仅包含字母的字符串,忽略大小写的区别,位置是由一对整数表示的坐标,它们之间用空格分隔。格式为Name x y。输入文件剩下的部分描述了这些人的缘分。每一行的格式为Name1 Name2 p。Name1和Name2为有缘人的姓名,p是他们之间的缘分值(p为小于等于255的正整数)。以一个End作为文件结束标志。每两个人之间的缘分至多只被描述一次。如果没有被描述,则说明他们缘分值为1。输出文件格式: 输出文件仅一个正整数,表示每一对被射中的人之间的缘分的总和。这个和应当是最大的。输入样例(cupid.in):230 0 Adam1 1 Jack0 2 George1 0 Victoria0 1 Susan1 2 CathyAdam Cathy 100Susan George 20George Cathy 40Jack Susan 5Cathy Jack 30Victoria Jack 20Adam Victoria 15End输出样例(cupid.out):65這是一道最佳匹配問題,用KM算法可以解決。
首先建圖,若兩點距離大於d或連線中有第三者,則權值賦為負無窮,保證這條邊永遠不能被取到。
具體細節見程序注釋。
Accode:
#include <cstdio>#include <iostream>#include <cstring>#include <cstdlib>#include <bitset>#include <map>using namespace std;const char fi[] = "cupid.in";const char fo[] = "cupid.out";const int maxN = 50;const int MAX = 0x3fffff00;const int MIN = -MAX;map <string, int> _boy, _girl;int w[maxN][maxN];int lx[maxN], ly[maxN];int bx[maxN], by[maxN];int gx[maxN], gy[maxN];int Link[maxN];bitset <maxN> boy, girl;int n, d, t;string str, str1, str2; void init_file() { freopen(fi, "r", stdin); freopen(fo, "w", stdout); std::ios::sync_with_stdio(false); } inline bool line(int x1, int y1, int x2, int y2, int x3, int y3) { int _x1 = x2 - x1; int _y1 = y2 - y1; int _x2 = x2 - x3; int _y2 = y2 - y3; if (_x1 * _y2 != _y1 * _x2) return false; if (_x1 * _x2 + _y1 * _y2 < 0) return true; return false; } //判斷(x2, y2)是否在(x1, y1)與(x3, y3)的連線上。 inline int sqr(int x) {return x * x; } inline void ltu(string &str) { string::iterator iter; for (iter = str.begin(); iter != str.end(); ++iter) if ((*iter) >= 'a' && (*iter) <= 'z') (*iter) -= 'a' - 'A'; } //小寫轉大寫。 void readdata() { cin >> d >> n; for (int i = 1; i < n + 1; ++i) { cin >> bx[i] >> by[i] >> str; ltu(str); _boy.insert(make_pair(str, i)); } for (int j = 1; j < n + 1; ++j) { cin >> gx[j] >> gy[j] >> str; ltu(str); _girl.insert(make_pair(str, j)); } while (1) { cin >> str1 >> str2 >> t; ltu(str1); ltu(str2);//由於題目中忽略大小寫,所以將小寫全部轉化為大寫。 if (str1 == "END") break; if (_boy.find(str1) == _boy.end()) swap(str1, str2); w[_boy[str1]][_girl[str2]] = t; } } void Modify() { for (int i = 1; i < n + 1; ++i) lx[i] = MIN;//頂標初始為負無窮。 for (int i = 1; i < n + 1; ++i) for (int j = 1; j < n + 1; ++j) { if (!w[i][j]) w[i][j] = 1; if (sqr(bx[i] - gx[j]) + sqr(by[i] - gy[j]) > sqr(d)) {w[i][j] = MIN; continue; } if (w[i][j] > 0) { for (int i1 = 1; i1 < n + 1; ++i1) if (i != i1) if (line(bx[i], by[i], bx[i1], by[i1], gx[j], gy[j])) {w[i][j] = MIN; break; } } else continue;//枚舉查看連線中間是否有其他男生。 if (w[i][j] > 0) { for (int j1 = 1; j1 < n + 1; ++j1) if (j != j1) if (line(bx[i], by[i], gx[j1], gy[j1], gx[j], gy[j])) {w[i][j] = MIN; break; } } else continue;//枚舉查看連線中間是否有其他女生。 lx[i] = max(lx[i], w[i][j]);//更新頂標。 } } bool Find(int u) { boy.set(u); for (int v = 1; v < n + 1; ++v) if (lx[u] + ly[v] == w[u][v] && !girl.test(v)) { girl.set(v); if (!Link[v] || Find(Link[v])) {Link[v] = u; return true; } } return false; } void work() { for (int k = 1; k < n + 1; ++k) while (1) { boy.reset(); girl.reset(); if (Find(k)) break; int Min = MAX; for (int i = 1; i < n + 1; ++i) if (boy.test(i)) for (int j = 1; j < n + 1; ++j) if (!girl.test(j)) Min = min(Min, lx[i] + ly[j] - w[i][j]); for (int i = 1; i < n + 1; ++i) { if (boy.test(i)) lx[i] -= Min; if (girl.test(i)) ly[i] += Min; } } int ans = 0; for (int j = 1; j < n + 1; ++j) ans += w[Link[j]][j]; printf("%d", ans); }int main(){ init_file(); readdata(); Modify(); work(); exit(0);}
第二次做:
#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <string>#include <map>using namespace std;const char fi[] = "cupid.in";const char fo[] = "cupid.out";const int maxN = 40;const int MAX = 0x3f3f3f3f;const int MIN = ~MAX;struct vec{ int x, y; vec() {} vec(int x, int y): x(x), y(y) {} vec operator-(const vec &b) const {return vec(x - b.x, y - b.y);} int operator+(const vec &b) const {return x * b.x + y * b.y;} int operator*(const vec &b) const {return x * b.y - y * b.x;} int norm() const {return x * x + y * y;} bool btwn(const vec &A, const vec &B) const { vec OA = vec(A.x - x, A.y - y), OB = vec(B.x - x, B.y - y); return !(OA * OB) && OA + OB < 0; } };map <string, int> _boy, _girl;vec boy[maxN], girl[maxN];bool b[maxN], g[maxN];int lx[maxN], ly[maxN], Link[maxN];int mp[maxN][maxN], n, K;void init_file(){ freopen(fi, "r", stdin); freopen(fo, "w", stdout); return;}void readdata(){ cin >> K >> n; for (int i = 0; i < n; ++i) { int x, y; string str; cin >> x >> y >> str; boy[i] = vec(x, y); transform(str.begin(), str.end(), str.begin(), ::tolower);//将一个string类型转换为小写。_boy[str] = i; } for (int i = 0; i < n; ++i) { int x, y; string str; cin >> x >> y >> str; girl[i] = vec(x, y); transform(str.begin(), str.end(), str.begin(), ::tolower); _girl[str] = i; Link[i] = -1; } int w; string s1, s2; while (cin >> s1 >> s2 >> w) { transform(s1.begin(), s1.end(), s1.begin(), ::tolower); transform(s2.begin(), s2.end(), s2.begin(), ::tolower);if (_boy.find(s1) == _boy.end()) swap(s1, s2); mp[_boy[s1]][_girl[s2]] = w; } return;}void modify(){ for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) { if (!mp[i][j]) mp[i][j] = 1; if ((boy[i] - girl[j]).norm() > K * K) {mp[i][j] = MIN; continue;} if (mp[i][j] > 0) for (int i1 = 0; i1 < n; ++i1) if (i1 != i && boy[i1].btwn(boy[i], girl[j])) {mp[i][j] = MIN; break;} if (mp[i][j] > 0) for (int j1 = 0; j1 < n; ++j1) if (j1 != j && girl[j1].btwn(boy[i], girl[j])) {mp[i][j] = MIN; break;} lx[i] = max(lx[i], mp[i][j]); } //当不能连边时一定要把权值设为负无穷。 return;}bool Find(int i){ b[i] = 1; for (int j = 0; j < n; ++j) if (lx[i] + ly[j] == mp[i][j] && !g[j]) { g[j] = 1; if (Link[j] == -1 || Find(Link[j])) { Link[j] = i; return 1; } } return 0;}void work(){ for (int k = 0; k < n; ++k) while (1) { memset(b, 0, sizeof b); memset(g, 0, sizeof g); if (Find(k)) break; int Min = MAX; for (int i = 0; i < n; ++i) if (b[i]) for (int j = 0; j < n; ++j) if (!g[j]) Min = min(Min, lx[i] + ly[j] - mp[i][j]); for (int i = 0; i < n; ++i) { if (b[i]) lx[i] -= Min; if (g[i]) ly[i] += Min; } } int ans = 0; for (int j = 0; j < n; ++j) ans += mp[Link[j]][j]; printf("%d\n", ans); return;}int main(){ init_file(); readdata(); modify(); work(); return 0;}
- ★☆【二分圖最佳匹配】丘比特的煩惱
- 【二分图最佳匹配】丘比特的烦恼
- BZOJ 2539 [Ctsc2000]丘比特的烦恼 带权二分图的最佳匹配
- 【二分图匹配】【CSTC2000】丘比特的烦恼
- 二分图的最佳匹配
- wikioi 丘比特的烦恼 (最大权匹配)
- 【二分圖最佳匹配】糊塗的記者
- KM算法 二分图的最佳匹配
- 二分图的最佳匹配(KM 算法)
- poj2195 二分图的最佳匹配
- Tour-----最佳二分匹配
- 二分图最佳匹配
- 【最大费用流】【最优匹配】丘比特的烦恼 Vijos 1169
- 二分图最佳匹配 入门
- 二分图最佳匹配 题目
- 二分图最佳完美匹配
- 二分图最佳完美匹配
- hdu2255 二分图最佳匹配
- poj2452
- POJ1350 Cabric Number Problem [模拟]
- 算法应用
- 纪念为“第九维”而死的脑细胞
- lucky sum of digit
- ★☆【二分圖最佳匹配】丘比特的煩惱
- Redis源代码分析之二:散列表——Dict(上)
- VB工程--百例49--显示文件路径
- CentOS 网络配置
- 用boost asio 获取本地IP遇到"host not found"的问题
- 又在折腾SQL05的安装
- android 启动线程注意的问题
- 整理项目代码
- poj2586