【最大费用流】【最优匹配】丘比特的烦恼 Vijos 1169
描述
隨著社會的不斷發展,人與人之間的感情越來越功利化。最近,愛神丘比特發現,愛情也已不再是完全純潔的了。這使得丘比特很是苦惱,他越來越難找到合適的男女,并向他們射去丘比特之箭。于是丘比特千里迢迢遠赴中國,找到了掌管東方人愛情的神——月下老人,向他求教。
月下老人告訴丘比特,純潔的愛情并不是不存在,而是他沒有找到。在東方,人們講究的是緣分。月下老人只要做一男一女兩個泥人,在他們之間連上一條紅線,那么它們所代表的人就會相愛——無論他們身處何地。而丘比特的愛情之箭只能射中兩個距離相當近的人,選擇的范圍自然就小了很多,不能找到真正的有緣人。
丘比特聽了月下老人的解釋,茅塞頓開,回去之后用了人間的最新科技改造了自己的弓箭,使得丘比特之箭的射程大大增加。這樣,射中有緣人的機會也增加了不少。
情人節(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; }總結
以上是生活随笔為你收集整理的【最大费用流】【最优匹配】丘比特的烦恼 Vijos 1169的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中学教学参考杂志中学教学参考编辑部中学教
- 下一篇: ABCD 谁是小偷