bzoj2539 丘比特的煩惱、黑書P333 (最優二分圖匹配)
丘比特的煩惱
題目描述 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的正整數
思路:
就是建圖跑二分圖最優匹配,這裏我用最小費用流求解。
幾個需要註意的地方:
- 點以坐標形式給出,要運用一下計算機和的知識求出兩點間距離、判斷兩點所連線段上是否有其他點;
- 點的特征值是一個字符串,且不區分大小寫,需要註意;
- 題面保證沒有重邊但數據中有重邊,且是取最後給出的邊,這裏比較坑。一是保證沒有重邊卻出現重邊debug了很久;二是想到可能存在重邊但沒想到竟不是取最好的邊debug了更久(不過使用領接矩陣的話恰好跳過了這個坑點,大概標程寫的KM吧);
- 數據中未給出的邊存在默認權值為一的邊,優先級低於距離限制和夾點限制。
代碼:
#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 (最優二分圖匹配)