BZOJ4819 新生舞会

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;
}

时间: 2024-10-15 06:30:14

BZOJ4819 新生舞会的相关文章

[BZOJ4819][SDOI2017]新生舞会

[BZOJ4819][SDOI2017]新生舞会 bzoj luogu 题意 有\(n\)个男孩子和\(n\)个女孩子.他们之间要两两结伴跳舞. 已知第\(i\)个男孩子和第\(j\)个女孩子结伴跳舞会有两个参数\(a_{i,j}\)和\(b_{i,j}\). 现在要求一个安排方案使得\(a_{i,j}\)的总和除以\(b_{i,j}\)的总和的商尽量大. 形式化地,就是求一个长度为\(n\)的排列\(\{p_i\}\),最大化\(L=\frac{\sum_{i=1}^{n}a_{i,p_i}}

【bzoj4819】[Sdoi2017]新生舞会 分数规划+费用流

题目描述 学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴.有n个男生和n个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴.Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 a[i][j] ,表示第i个男生和第j个女生一起跳舞时他们的喜悦程度.Cathy还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 b[i][j],表示第i个男生和第j个女生一起跳舞时的不协调程度.当然,还需要考虑很多其他问题.Cathy想先用一个程序通过a

BZOJ4819: [Sdoi2017]新生舞会(01分数规划)

Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1029  Solved: 528[Submit][Status][Discuss] Description 学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴.有n个男生和n个女生参加舞会 买一个男生和一个女生一起跳舞,互为舞伴.Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 a[i][j] ,表示第i个男生和第j个女生一起跳舞时他们的喜悦程度.Cathy

[Sdoi2017]新生舞会

[Sdoi2017]新生舞会 http://www.lydsy.com/JudgeOnline/problem.php?id=4819 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴.有n个男生和n个女生参加舞会 买一个男生和一个女生一起跳舞,互为舞伴.Cathy收集了这些同学之间的关系,比如两个人之前认识没计算

bzoj 4819: [Sdoi2017]新生舞会

Description 学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴.有n个男生和n个女生参加舞会 买一个男生和一个女生一起跳舞,互为舞伴.Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 a[i][j] ,表示第i个男生和第j个女生一起跳舞时他们的喜悦程度.Cathy还需要考虑两个人一起跳舞是否方便, 比如身高体重差别会不会太大,计算得出 b[i][j],表示第i个男生和第j个女生一起跳舞时的不协调程度.当然, 还需要考虑很多其他问题.Cathy

BZOJ 4819 新生舞会

第一句话:算出3.363636的孩子啊,你跑错流种了. 貌似上一篇我讲SDOI出原题?嘿还真是! 半个月前有一个叫WG的男人给我们搞过一场考试... ... 里面有一道题叫做保密... ...SDOI2011... ... 对于每个点求ΣS/ΣT最值然后转跑浮点数网络流... ... 是不是感觉我在讲这道题的正解... ...没错正解就是这样... ...所以我说是原题吧. 跟 保密 是一样的思路.求Σa/Σb的最值,可以用二分答案来做. Σa/Σb的最大值怎么求呢?设一个当前答案ans; 显然

codevs 5965 [SDOI2017]新生舞会

分数规划的裸题. 不会分数规划的OIer.百度:胡伯涛<最小割模型在信息学竞赛中的应用> /* TLE1: last:add(i,j+n,1e9,(real)((real)a[i][j]-ans*(real)b[i][j])); now :add(i,j+n,1,(real)((real)a[i][j]-ans*(real)b[i][j])); TLE2: last:real l=eps,r=(real)u/(real)v,mid,ans=0,now; now :real l=0,r=(rea

蓝桥杯 ALGO-94 新生舞会(结构体)

[思路]:怒刷水题的节奏.. [AC代码]: #include <iostream> #include <algorithm> #include <string> #include <cstdio> #include <cstring> using namespace std; #define MAX 1000+2 #define MAX_NAME_NUM 20+2 struct Student { char name[MAX_NAME_NUM+

「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)

学习了新姿势..(一直看不懂大爷的代码卡了好久T T 首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2*s_l-l$的区间$[l+1,r]$其众数为$x$,这个显然可以用一个数据结构来维护. 直接扫一遍效率是$O($数字种类数$*nlogn)$的,无法承受,但是我们发现,对于每一段非$x$的数,$2*s_i-i$是公差为$-1$的等差数列,所以它们对答案的贡献实际上可以一次性计算.设$L$为一段非$x$数