4819: [Sdoi2017]新生舞会
Time Limit: 10 Sec Memory Limit: 128 MB
Description
学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。有n个男生和n个女生参加舞会
买一个男生和一个女生一起跳舞,互为舞伴。Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出
a[i][j] ,表示第i个男生和第j个女生一起跳舞时他们的喜悦程度。Cathy还需要考虑两个人一起跳舞是否方便,
比如身高体重差别会不会太大,计算得出 b[i][j],表示第i个男生和第j个女生一起跳舞时的不协调程度。当然,
还需要考虑很多其他问题。Cathy想先用一个程序通过a[i][j]和b[i][j]求出一种方案,再手动对方案进行微调。C
athy找到你,希望你帮她写那个程序。一个方案中有n对舞伴,假设没对舞伴的喜悦程度分别是a‘1,a‘2,...,a‘n,
假设每对舞伴的不协调程度分别是b‘1,b‘2,...,b‘n。令
C=(a‘1+a‘2+...+a‘n)/(b‘1+b‘2+...+b‘n),Cathy希望C值最大。
Input
第一行一个整数n。
接下来n行,每行n个整数,第i行第j个数表示a[i][j]。
接下来n行,每行n个整数,第i行第j个数表示b[i][j]。
1<=n<=100,1<=a[i][j],b[i][j]<=10^4
Output
一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等
Sample Input
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
Sample Output
5.357143
题解
分数规划 + 网络流
二分C值,设当前C值为C‘, 每对人对答案的贡献为\( a - b * C ^ ‘ \),费用流使贡献总和最大,为正则说明C‘可行。
使用迭代会使效率更高,每次费用流都是在找一个尽量更优或可行的答案,所以我们把费用流跑出的方案记录下来,作为下一次的C‘值。
每次迭代结束比较上一次的C‘与现在跑出来的C‘‘,差的绝对值小于eps则找到最优解,C‘的初始值任意。
代码
/* Stay hungry, stay foolish. */ #include<iostream> #include<algorithm> #include<queue> #include<string> #include<map> #include<vector> #include<set> #include<sstream> #include<stack> #include<ctime> #include<cmath> #include<cctype> #include<climits> #include<cstring> #include<cstdio> #include<cstdlib> #include<iomanip> #include<bitset> #include<complex> using namespace std; /* #define getchar() getc() char buf[1<<15],*fs,*ft; inline char getc() {return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft)?0:*fs++;} */ template <class _T> inline void read(_T &_x) { int _t; bool _flag=false; while((_t=getchar())!=‘-‘&&(_t<‘0‘||_t>‘9‘)) ; if(_t==‘-‘) _flag=true,_t=getchar(); _x=_t-‘0‘; while((_t=getchar())>=‘0‘&&_t<=‘9‘) _x=_x*10+_t-‘0‘; if(_flag) _x=-_x; } typedef long long LL; const int maxn = 500; const int maxm = 100000; const double eps = 1e-7; struct Edge { int v, from, nxt, flow; double cost; Edge() {} Edge(int a, int b, int c, double d): v(a), nxt(b), flow(c), cost(d) {} }e[maxm]; int fir[maxn], ecnt, pre[maxn], preu[maxn]; double dis[maxn]; bool vis[maxn]; inline void addedge(int a, int b, int c, double d) { e[++ecnt] = Edge(b, fir[a], c, d), fir[a] = ecnt; e[++ecnt] = Edge(a, fir[b], 0, -d), fir[b] = ecnt; } int n, sink, a[maxn][maxn], b[maxn][maxn]; inline bool spfa() { memset(dis, 0xfe, sizeof (double) * (sink + 1)); memset(vis, false, sizeof (bool) * (sink + 1)); double DMIN = dis[0]; queue<int> q; dis[0] = 0, q.push(0); while (!q.empty()) { int now = q.front(); q.pop(); vis[now] = false; for (int u = fir[now]; u; u = e[u].nxt) if (e[u].flow) { if (dis[e[u].v] < dis[now] + e[u].cost) { dis[e[u].v] = dis[now] + e[u].cost; pre[e[u].v] = now, preu[e[u].v] = u; if (!vis[e[u].v]) { vis[e[u].v] = true; q.push(e[u].v); } } } } return dis[sink] != DMIN; } double augment() { int now = sink; while (now != 0) { e[preu[now]].flow -= 1; e[preu[now] ^ 1].flow += 1; now = pre[now]; } return dis[sink]; } double mcmf() { double ret = 0; while (spfa()) { ret += augment(); } return ret; } double Run(double ans) { ecnt = 1; memset(fir, 0, sizeof (int) * (sink + 1)); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { addedge(i, j + n, 1, a[i][j] - ans * b[i][j]); } } for (int i = 1; i <= n; ++i) addedge(0, i, 1, 0); for (int i = 1; i <= n; ++i) addedge(n + i, sink, 1, 0); mcmf(); int ansa = 0, ansb = 0; for (int i = 1; i <= n; ++i) { for (int u = fir[i]; u; u = e[u].nxt) { if (e[u].flow == 0 && e[u].v > n && e[u].v < sink) { ansa += a[i][e[u].v - n], ansb += b[i][e[u].v - n]; } } } return (double)ansa / ansb; } int main() { //freopen(".in","r",stdin); //freopen(".out","w",stdout); read(n); sink = n + n + 1; for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) read(a[i][j]); for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) read(b[i][j]); double prev, ans = 0; do { prev = ans, ans = Run(ans); } while (fabs(ans - prev) > eps); printf("%.6lf", ans); return 0; }