#2003. 「SDOI2017」新生舞会
题目描述
学校组织了一次新生舞会,Cathy 作为经验丰富的老学姐,负责为同学们安排舞伴。
有 n nn 个男生和 n nn 个女生参加舞会,一个男生和一个女生一起跳舞,互为舞伴。
Cathy 收集了这些同学之间的关系,比如两个人之前是否认识,计算得出 ai,j a_{i, j}ai,j,表示第 i ii 个男生和第 j jj 个女生一起跳舞时他们喜悦程度。
Cathy 还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 bi,j b_{i, j}bi,j 表示第 i ii 个男生和第 j jj 个女生一起跳舞时的不协调裎度。
当然,还需要考虑很多其他间题。
Cathy 想先用一个程序通过 ai,j a_{i, j}ai,j 和 bi,j b_{i, j}bi,j 求出一种方案,再手动对方案进行微调。
Cathy 找到你,希望你帮她写那个程序。
一个方案中有 n nn 对舞伴,假设每对舞伴的喜悦程度分别是 a1′,a2′,…,an′ a'_1, a'_2, \ldots, a'_na1′,a2′,…,an′,假设每对舞伴不协调程度分别是 b1′,b2′,…,bn′ b'_1, b'_2, \ldots, b'_nb1′,b2′,…,bn′。令
C=a1′+a2′+⋯+an′b1′+b2′+⋯+bn′ C = \frac{a'_1 + a'_2 + \cdots + a'_n}{b'_1 + b'_2 + \cdots + b'_n}C=b1′+b2′+⋯+bn′a1′+a2′+⋯+an′Cathy 希望 C 值最大。
输入格式
第一行一个整数 n nn。
接下来 n nn 行,每行 n nn 个正整数,第 i ii 行第 j jj 个数表示 ai,j a_{i, j}ai,j。
接下来 n nn 行,每行 n nn 个正整数,第 i ii 行第 j jj 个数表示 bi,j b_{i, j}bi,j。
输出格式
一行一个数,表示 C CC 的最大值。四舍五入保留六位小数,选手输出的小数需要与标准输出相等。
样例
样例输入
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
样例输出
5.357143
数据范围与提示对于 10% 10\%10% 的数据,1≤n≤5 1 \leq n \leq 51≤n≤5;
对于 40% 40\%40% 的数据,1≤n≤18 1 \leq n \leq 181≤n≤18;
另外存在 20% 20\%20% 的数据,bi,j=1 b_{i, j} = 1bi,j=1;
对于 100% 100\%100% 的数据,1≤n≤100,1≤ai,j≤104,1≤bi,j≤104 1 \leq n \leq 100, 1 \leq a_{i, j} \leq 10 ^ 4,1 \leq b_{i, j} \leq 10 ^ 41≤n≤100,1≤ai,j≤104,1≤bi,j≤104。
传送一篇来自苣苣的博客:01分数规划总结
感觉01分数规划就是先二分答案,然后再求最值。
题目链接:https://loj.ac/problem/2003
题意:选出n对舞伴,使得价值和/代价和最大。
思路:基础01分数规划+最大权匹配(可以使用最小费用流求解,先对费用取负数,最后答案再取负数)
代码:
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<set> #include<queue> #include<stack> #include<map> #include<vector> using namespace std; typedef long long ll; typedef pair<int,int> P; const int maxn=200,maxm=1e5+100,inf=0x3f3f3f3f,mod=1e9+7; const ll INF=1e18+7; const double eps=1e-10; int a[maxn][maxn],b[maxn][maxn]; struct edge { int from,to; int c; int a,b; }; vector<edge>es; vector<int>G[maxn]; double dist[maxn]; int pre[maxn]; void addedge(int u,int v,int c,int a,int b) { es.push_back((edge) { u,v,c,a,b }); es.push_back((edge) { v,u,0,-a,-b }); int x=es.size(); G[u].push_back(x-2); G[v].push_back(x-1); } bool SPFA(int s,int t,double mid) { static std::queue<int> q; static bool inq[maxn]; for(int i=0; i<maxn; i++) dist[i]=1.0*inf,inq[i]=false; pre[s]=-1; dist[s]=0.0; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); inq[u]=false; for(int i=0; i<G[u].size(); i++) { edge e=es[G[u][i]]; double d=0.0; if(e.a&&e.b) d=1.0*e.a-mid*e.b; if(e.c&&dist[e.to]>dist[u]-d) { pre[e.to]=G[u][i]; dist[e.to]=dist[u]-d; if(!inq[e.to]) q.push(e.to),inq[e.to]=true; } } } return fabs(inf-dist[t])>eps; } double dinic(int s,int t,int f,double mid) { int flow=0; double cost=0.0; while(SPFA(s,t,mid)) { int d=f; for(int i=t; i!=s; i=es[pre[i]].from) d=min(d,es[pre[i]].c); f-=d; flow+=d,cost+=d*dist[t]; for(int i=t; i!=s; i=es[pre[i]].from) { es[pre[i]].c-=d; es[pre[i]^1].c+=d; } if(f<=0) break; } for(int i=0; i<es.size(); i+=2) es[i].c=1,es[i+1].c=0; //cout<<-1*cost<<" ******"<<endl<<endl; return -1*cost; } bool check(double mid,int s,int t,int n) { return dinic(s,t,n,mid)>=eps?true:false; } int main() { int n,m; scanf("%d",&n); for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) scanf("%d",&a[i][j]); } for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) scanf("%d",&b[i][j]); } int s=0,t=2*n+1; for(int i=1; i<=n; i++) addedge(s,i,1,0,0); for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) addedge(i,j+n,1,a[i][j],b[i][j]); } for(int i=1; i<=n; i++) addedge(i+n,t,1,0,0); double l=0.0,r=1000000000.0; double ans=0.0; while((r-l)>eps) { double mid=(l+r)/2; //printf("%.8f %.8f %.8f\n",l,r,mid); if(check(mid,s,t,n)) l=mid,ans=l; else r=mid; } printf("%.6f\n",ans); return 0; }