传送门:http://oj.cnuschool.org.cn/oj/home/problem.htm?problemID=1037
试题描述:
信息学社团已经逐渐发展壮大,成员也越来越多。现在,有n个同学有了关于信息学的问题,公务繁忙的杨老师决定派出n个小助手来帮助他们解答问题。现在问题来了,每个小助手和它对应的同学不能离的太远(要不然杨老师会觉得场面很乱…),也就是说两个人的距离不能超过杨老师的规定值Distance。同时,俩个人的中间不能隔有其他同学(要不然同学们会互相干扰的…),就是说在两个同学a,b所连成的线段上不能有同学c。现在,每解答一个问题(我们认为小助手足够聪明,只要有匹配的人都可以解答他们的问题),杨老师都会奖励小助手一些积分,现在,贪心的小助手们联合起来想让他们获取的积分总数尽量大…请你帮小助手们编写一个程序来算出他们能获得的最大的积分数。(一些题目补充见“其他说明”)
输入:
第一行是一个正整数Distance表示杨老师规定的最大范围。
第二行是一个正整数n,表示有n个同学。
接下来的n行是每个同学的x,y坐标及他们的名字。再接下来的n行是每个小助手的x,y坐标及他们的名字。
输入文件剩下的部分描述了一些同学与特定小助手解答完问题能获得的杨老师的奖励。每一行的格式为Name1 Name2 p。Name1和Name2为同学,小助手的姓名,p是他们之间的可以获得的积分。以一个End作为文件结束标志。每两个人之间的奖励积分如果被描述多次,则以最后一次的描述为准。如果没有被描述,则说明他们奖励积分为1。
输出:
一个正整数w,表示小助手们能获得的最多的积分数。
输入示例:
2 3
0 0 zYT
1 1 WJh
0 2 XYz
1 0 XJR
0 1 WZJ
1 2 LZJ
ZYt LZJ 100
WZJ XYZ 20
XYZ LZJ 40
WJH WzJ 5
WJH LZJ 30
XJR WJH 20
ZYT XJR 15
End
输出示例:
65
其他说明:
一些小问题:
首先:我们假设同学和小助手都不会移动……
其次:我们假设杨老师手中有无限积分……
然后:每个人的名字由于OI出现问题导致大小写随机了,你的程序需要能够正确识别纠错。(具体请看样例)
再然后:人们的姓名是长度小于200且仅包含字母的字符串(不是汉字)。
再再然后:保证给出的奖励分数合法,即小助手和小助手不会给出奖励值,同学和同学不会给出奖励值。
再再再然后:一个小助手只能帮助一个同学,一个同学只能被一个小助手帮助。
再再再再然后:最后的结果保证在int范围内。
最后声明:由于数据给的很小,时间卡紧了一点。
数据范围:
1 <= n < 30
1 <= Distance < 10^6
1 <= x,y < 10^6
1 <= p <= 1500
以上所有变量均属于整数集。
题解:
【裸体二分图最佳完美匹配】小助手和同学形成了天然的二分图。用向量判重线、预处理两点间距离平方判距离来连边。
KM的:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std; void read(int& x)
{
x = ;
int sig = ;
char ch = getchar(); while(!isdigit(ch))
{
if(ch == '-') sig = -;
ch = getchar();
} while(isdigit(ch))
{
x = * x + ch - '';
ch = getchar();
} x *= sig;
return ;
} const int MAXN = + ;
const int MAXBIGN = + ;
const int MAXL = + ;
const int INF = -1u >> ; struct Person
{
double x, y;
char Name[MAXL];
}P[MAXN]; int N, M, T; double Distance; int adj[MAXN][MAXN], Match[MAXN], L[MAXN];
int slack[MAXN]; bool vis[MAXN]; double Dist(Person a, Person b)
{
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
} bool Is_Cross(Person a, Person b, Person c)
{
if((a.x - b.x) * (b.y - c.y) == (b.x - c.x) * (a.y - b.y))
{
if(a.y == c.y) return (a.x < b.x && b.x < c.x) || (c.x < b.x && b.x < a.x);
else return (a.y < b.y && b.y < c.y) || (c.y < b.y && b.y < a.y);
} return false;
} void Try_Shot()
{
for(int i = ; i <= N; i++)
{
for(int j = M; j <= T; j++)
{
if(Dist(P[i], P[j]) > Distance) adj[i][j] = ;
else
{
int k;
for(k = ; k <= T; k++)
if(k != i && k != j && Is_Cross(P[i], P[k], P[j])) break; if(k > T) adj[i][j] = ;
else adj[i][j] = ;
}
}
} return ;
} int Scanname(char *S)
{
for(int i = ; i <= T; i++)
if(strcmp(P[i].Name, S) == ) return i; return -;
} void Format(char *S)
{
for(; *S; S++)
if(*S >= 'a' && *S <= 'z') *S = *S - 'a' + 'A'; return ;
} void KM_Init()
{
int p1, p2;
int v; char Name[MAXL]; scanf("%lf", &Distance);
Distance = Distance * Distance; read(N); M = N + ;
T = N + N; for(int i = ; i <= T; i++)
{
scanf("%lf%lf", &P[i].x, &P[i].y); scanf("%s", P[i].Name);
Format(P[i].Name);
} Try_Shot(); while(scanf("%s", Name) != EOF)
{
if(strcmp(Name, "End") == ) break;
Format(Name);
p1 = Scanname(Name); scanf("%s", Name);
Format(Name);
p2 = Scanname(Name); read(v); if(p1 > p2) swap(p1, p2); if(adj[p1][p2] != ) adj[p1][p2] = v;
} return ;
} bool Path(int i)
{ vis[i] = true; for(int j = M; j <= T; j++)
{
if(!vis[j] && adj[i][j] > )
{
int t = L[i] + L[j] - adj[i][j];
if(t == )
{
vis[j] = true;
if(Match[j] == || Path(Match[j]))
{
Match[j] = i;
return true;
}
}
else slack[j] = min(slack[j], t);
}
} return false;
} void KM_Solve()
{
int delta; for(int i = ; i <= N; i++)
for(int j = M; j <= T; j++)
L[i] = max(L[i], adj[i][j]); for(int i = ; i <= N; i++)
{
while()
{
memset(vis, , sizeof(vis));
for(int j = M; j <= T; j++) slack[j] = INF;
if(Path(i)) break; delta = INF; for(int k = M; k <= T; k++)
if(!vis[k] && slack[k] < delta)
delta = slack[k]; for(int j = ; j <= N; j++)
if(vis[j]) L[j] -= delta; for(int j = M; j <= T; j++)
if(vis[j]) L[j] += delta;
}
}
} void Print()
{
int Ans = ;
for(int i = M; i <= T; i++)
Ans += adj[Match[i]][i]; printf("%d\n", Ans); return ;
} int main()
{
KM_Init();
KM_Solve();
Print(); return ;
}
费用流的:
#include<cstdio>
#include<iostream>
#include<cctype>
#include<queue>
#include<map>
#include<cstring>
#include<algorithm>
#define dist(i,j) (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])
using namespace std;
const int INF=;
const int maxn=;
const int maxm=;
struct Edge
{
int from,to,cap,flow,cost;
};
struct MCMF
{
int n,m;
int first[maxn],next[maxm];
Edge edges[maxm];
int d[maxn],p[maxn],a[maxn],inq[maxn];
void init(int n)
{
this->n=n;
m=;
memset(first,-,sizeof(first));
}
void AddEdge(int from,int to,int cap,int cost)
{
edges[m]=(Edge){from,to,cap,,cost};
next[m]=first[from];
first[from]=m++;
edges[m]=(Edge){to,from,,,-cost};
next[m]=first[to];
first[to]=m++;
}
int BellmanFord(int s,int t,int& flow,int& cost)
{
queue<int> Q;
memset(inq,,sizeof(inq));
for(int i=;i<=n;i++) d[i]=INF;
inq[s]=; d[s]=; a[s]=INF; Q.push(s);
while(!Q.empty())
{
int x=Q.front(); Q.pop();
inq[x]=;
for(int i=first[x];i!=-;i=next[i])
{
Edge& e=edges[i];
if(e.cap>e.flow&&d[e.to]>d[x]+e.cost)
{
d[e.to]=d[x]+e.cost;
a[e.to]=min(a[x],e.cap-e.flow);
p[e.to]=i;
if(!inq[e.to])
{
inq[e.to]=;
Q.push(e.to);
}
}
}
}
if(d[t]==INF) return ;
flow+=a[t]; cost+=a[t]*d[t];
int x=t;
while(x!=s)
{
edges[p[x]].flow+=a[t];
edges[p[x]^].flow-=a[t];
x=edges[p[x]].from;
}
return ;
}
int solve(int s,int t)
{
int flow=,cost=;
while(BellmanFord(s,t,flow,cost));
return cost;
}
}sol;
double maxdist,x[maxn],y[maxn];
map<string,int> S;
int n,m,tot;
int id(string a)
{
int len=a.length();
for(int i=;i<len;i++) a[i]=tolower(a[i]);
if(!S.count(a))
{
S[a]=++tot;
return tot;
}
return S[a];
}
int can(int a,int c)
{
if((x[a]-x[c])*(x[a]-x[c])+(y[a]-y[c])*(y[a]-y[c])>maxdist*maxdist) return ;
for(int i=;i<=n*;i++) if(i!=a&&i!=c)
{
if((x[i]-x[a])*(x[i]-x[c])>) continue;
if((y[i]-y[a])*(y[i]-y[c])>) continue;
if((x[i]-x[a])*(y[c]-y[i])==(y[i]-y[a])*(x[c]-x[i])) return ;
}
return ;
}
int w[maxn][maxn];
int main()
{
scanf("%lf%d",&maxdist,&n);
sol.init(n*+);
double t1,t2;
for(int i=;i<=n;i++)
for(int j=n+;j<=n*;j++) w[i][j]=;
for(int i=;i<=n;i++)
{
string a;
scanf("%lf%lf",&t1,&t2);cin>>a;
int c=id(a);
x[i]=t1+;y[i]=t2+;
sol.AddEdge(n*+,i,,);
}
for(int i=;i<=n;i++)
{
string a;
scanf("%lf%lf",&t1,&t2);cin>>a;
int c=id(a);
x[i+n]=t1+;y[i+n]=t2+;
sol.AddEdge(i+n,*n+,,);
}
while()
{
string name;
cin>>name; if(name=="End") break;
int a,b,c;
a=id(name);
cin>>name;
b=id(name);
scanf("%d",&c);
if(a>b) swap(a,b);
w[a][b]=c;
}
for(int i=;i<=n;i++)
for(int j=n+;j<=n*;j++) if(can(i,j))
sol.AddEdge(i,j,,-w[i][j]);
printf("%d\n",-sol.solve(*n+,*n+));
return ;
}