主题:《丘比特的烦恼》
问题描述:
题目给出男女各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.
题目给出男女各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.