回 帖 发 新 帖 刷新版面

主题:《丘比特的烦恼》

问题描述:
  题目给出男女各n人,要求求出他们的最大费用的完备匹配。
问题分析:
  根据题意,我们可以把所有的男子看作二分图中左边的点,把女子看作二分图中右边的点。如果第i个男子和第j个女子可以匹配,就连一条从左边第i个点到右边第j个点的弧,弧的费用为他们之间的缘分值。例如样例数据的情况如下:

    输入样例(cupid.in):
    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
   
[img]http://www.chinaschool.org/aosai/lwjl/images/02-0220-01.gif[/img]
  由于题目数据较小(n<=30),所以我们可以使用邻接矩阵来储存任意两个男女间的关系。g[i,j]=0表示第i个男子和第j个女子不可能匹配,g[i,j]>0表示第i个男子和第j个女子的匹配费用为g[i,j]。
  首先必须对输入数据进行分析,转化成邻接矩阵g中的表示方法。设两人坐标为m(x,y),w(x,y)。如果两人不能匹配,这有以下两种情况:

  1. 两人的距离超过了丘比特箭的射程k。
    即 sqrt(sqr(m(x)-w(x))+sqr(m(y)-w(y)))>k
  2. 有一个人p(x,y)在两人中间,并且三人在同一直线上
    即 ((p[x]-m[x])*(p[y]-w[y])<=0)and((p[y]-m[y])*(p[y]-w[y])<=0)and
      (p[x]-m[x])*(w[y]-m[y])=(p[y]-m[y])*(w[x]-m[x])

注意:
  如果两人能匹配,则这两个人的小费用为1
  判断一个人在两个人中间时必须同时判断x,y两个坐标,并且乘积必须是小等零,而不是小于零。

  反例:(0,0) (1,0) (2,0)
  每个人的名字无大小写之分
  读入的两人的关系时,可能男在前也可能女在前
  在读入数据处理完成之后,就只要求出图的最大费用的完备匹配就可以了。在匹配的算法上,可以使用最大费用最大流或匈牙利算法来解决。

总结:
  本题在读入数据处理上比较麻烦,在编程时必须考虑可能出现的各种情况。
在最大费用的完备匹配上,由于数据较小,不需要什么优化。利用匈牙利算法,并用链表表示关系,至少可以把N扩大到500。


程序:
{$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R+,S+,T+,V+,X+}
{$M 16384,0,655360}
const input='Cupid.in5';
    output='Cupid.ou5';
var g:array[1..30,1..30]of integer;
  a,b,fa,fb,ca,cb:array[1..30]of longint;
  n,k:integer;
{procedure do1(x,y,z:integer);
 begin
  if (x<=n)and(y>n) then g[x,y-n]:=0;
  if (x<=n)and(z>n) then g[x,z-n]:=0;
  if (y<=n)and(z>n) then g[y,z-n]:=0;
  if (y<=n)and(x>n) then g[y,x-n]:=0;
  if (z<=n)and(x>n) then g[z,x-n]:=0;
  if (z<=n)and(y>n) then g[z,y-n]:=0;
 end; }
procedure init;
 var f:text;
  i,j,z,u1,v1:integer;
  u,v:string[20];
  c:char;
  d:array[1..60]of string[20];
  e:array[1..60]of record
        x,y:integer;
       end;
procedure do1(x,y,z:integer);
 begin
  if ((e[z].x>e[x].x)and(e[z].x<e[y].x))or
    ((e[z].y>e[x].y)and(e[z].y<e[y].y)) then
     if (x<=n)and(y>n) then g[x,y-n]:=0;
  if ((e[z].x<e[x].x)and(e[z].x>e[y].x))or
    ((e[z].y<e[x].y)and(e[z].y>e[y].y)) then
     if (x<=n)and(y>n) then g[x,y-n]:=0;
 end;
 begin
  assign(f,input);
  reset(f);
  readln(f,k);
  readln(f,n);
  for i:=1 to n do
   for j:=1 to n do
    g[i,j]:=1;
  for i:=1 to 2*n do
   begin
    readln(f,e[i].x,e[i].y,c,d[i]);
    for j:=1 to length(d[i]) do
     d[i,j]:=upcase(d[i,j]);
   end;
  repeat
   u:='';
   v:='';
   c:='a';
   while not(eof(f))and(c<>' ') do
    begin
     read(f,c);
     c:=upcase(c);
     if c<>' ' then u:=u+c;
    end;
   c:='a';
   while not(eof(f))and(c<>' ') do
    begin
     read(f,c);
     c:=upcase(c);
     if c<>' ' then v:=v+c;
    end;
   readln(f,z);
   if u<>'END' then
   begin
    for i:=1 to n*2 do
     begin
      if u=d[i] then u1:=i;
      if v=d[i] then v1:=i;
     end;
    if u1<v1 then g[u1,v1-n]:=z
    else g[v1,u1-n]:=z;
   end;
  until(u='END');
  close(f);
  for i:=1 to 2*n do
   for j:=1 to 2*n do
    for z:=1 to 2*n do
     if (i<>j)and(j<>k)and(i<>k) then
      if (e[z].x=e[i].x)or(e[j].x=e[i].x) then
       if e[j].x=e[z].x then do1(i,j,z)
      else
     else if (e[z].y=e[i].y)or(e[j].y=e[i].y) then
          if e[z].y=e[j].y then do1(i,j,z)
          else
        else if (e[z].x-e[i].x)/(e[j].x-e[i].x)=(e[z].y-                       e[i].y)/(e[j].y-e[i].y) then do1(i,j,z);
  for i:=1 to n do
   for j:=n+1 to 2*n do
    if sqr(e[i].x-e[j].x)+sqr(e[i].y-e[j].y)>k*k then
      g[i,j-n]:=0;
 end;
procedure do2;
 var develop,develops:boolean;
   i,j,maxn,max:longint;
 begin
  fillchar(a,sizeof(a),0);
  fillchar(b,sizeof(b),0);
  repeat
   develops:=false;
   fillchar(fa,sizeof(fa),0);
   fillchar(fb,sizeof(fb),0);
   fillchar(ca,sizeof(ca),0);
   fillchar(cb,sizeof(cb),0);
   for i:=1 to n do
     cb[i]:=-maxint;
   for i:=1 to n do
    if a[i]<>0 then ca[i]:=-maxlongint;
   repeat
    for i:=1 to n do
     if ca[i]<>-maxint then
      for j:=1 to n do
       if g[i,j]<>0 then
        if g[i,j]+ca[i]>cb[j] then
         begin
          cb[j]:=g[i,j]+ca[i];
          fb[j]:=i;
         end;
   develop:=false;
   for j:=1 to n do
    if b[j]<>0 then
     if ca[b[j]]<cb[j]-g[b[j],j] then
      begin
       develop:=true;
       ca[b[j]]:=cb[j]-g[b[j],j];
       fa[b[j]]:=j;
      end;
   until not(develop);
   maxn:=0;
   max:=-maxint;
   for i:=1 to n do
    if b[i]=0 then
     if cb[i]>max then
      begin max:=cb[i]; maxn:=i; end;
   j:=maxn;
   if j<>0 then
    begin
     develops:=true;
     repeat
      b[j]:=fb[j];
      a[fb[j]]:=j;
      j:=fb[j];
      j:=fa[j];
     until(j=0);
    end;
   until not(develops);
  end;
procedure pri;
 var i:integer;
   s:longint;
 begin
  s:=0;
  for i:=1 to n do
    if a[i]<>0 then
      s:=s+g[i,a[i]];
  writeln(s);
  readln;
 end;
begin
 init;
 do2;
 pri;
end.

回复列表 (共4个回复)

沙发

可不可以用C描述一下,谢谢

板凳

c++也好

3 楼

哪里抄的解体报告?

4 楼

这其实是一个图的搜索问题。

我来回复

您尚未登录,请登录后再回复。点此登录或注册