zoukankan      html  css  js  c++  java
  • BZOJ4819: [Sdoi2017]新生舞会(01分数规划)

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 1029  Solved: 528
    [Submit][Status][Discuss]

    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

    HINT

     

    Source

     
    洛谷居然卡邻接表,丧心病狂
    思路比较简单,裸的洞妖分数规划
    枚举一个ans
    然后从$S$向男生连一条流量为$1$,费用为$0$的边
    从每个女生向$T$连一条流量为$1$,费用为$0$的边
    从每个女生向每个男生连一条流量为$1$,费用为$a[i][j]-ans*b[i][j]$的边
    二分检验
    注意边的编号必须从$0$开始。
    注意精度
     
    // luogu-judger-enable-o2
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<cstdlib>
    #define INF 1e8+10
    using namespace std;
    const int MAXN=201;
    const double eps=1e-7;
    #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXN,stdin),p1==p2)?EOF:*p1++)
    char buf[1<<20],*p1=buf,*p2=buf;
    inline int read()
    {
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    struct node
    {
        int u,v,f,nxt;
        double w;
    }edge[MAXN*MAXN];
    int head[MAXN],num=0;
    int N,S,T;
    int a[233][233],b[233][233];
    double ans=0.0;
    inline void add_edge(int x,int y,int z,double k)
    {
        edge[num].u=x;
        edge[num].v=y;
        edge[num].f=z;
        edge[num].w=k;
        edge[num].nxt=head[x];
        head[x]=num++;
    }
    inline void AddEdge(int x,int y,int z,double k)
    {
        add_edge(x,y,z,k);
        add_edge(y,x,0,-k);
    }
    int arrive[MAXN],vis[MAXN],pre[MAXN];
    double dis[MAXN];
    bool SPFA()
    {
        queue<int>q;
        q.push(S);
        for(register int i=S;i<=T;i++) dis[i]=-1e20,arrive[i]=0;
        memset(vis,0,sizeof(vis));
        dis[S]=0;vis[S]=1;
        while(q.size()!=0)
        {
            int p=q.front();q.pop();
            vis[p]=0;arrive[p]=1;
            for(int i=head[p];i!=-1;i=edge[i].nxt)
            {
                if(edge[i].f&&dis[edge[i].v]<dis[p]+edge[i].w)
                {
                    dis[edge[i].v]=dis[p]+edge[i].w;
                    pre[edge[i].v]=i;
                    if(!vis[edge[i].v])
                        q.push(edge[i].v),vis[edge[i].v]=1;
                }
            }
        }
        return arrive[T];
    }
    int dfs()
    {
        int mn=INF;
        int now=T;
        while(pre[now])
        {
            mn=min(mn,edge[pre[now]].f);
            now=edge[pre[now]].u;
        }
        ans+=mn*dis[T];
        now=T;
        while(pre[now])
        {
            edge[pre[now]].f-=mn;
            edge[pre[now]^1].f+=mn;
            now=edge[pre[now]].u;
        }
    }
    bool check(double val)
    {
        memset(pre,0,sizeof(pre));
        memset(head,-1,sizeof(head));
        num=2;
        for(int i=1;i<=N;i++) AddEdge(S,i,1,0);
        for(int i=1;i<=N;i++) AddEdge(i+N,T,1,0);
        for(int i=1;i<=N;i++)    for(int j=1;j<=N;j++) AddEdge(i,j+N,1,a[i][j]-1.0*val*b[i][j]);
        ans=0.0;
        while(SPFA()) 
            dfs();
        if (ans<=0) return 1;
        else return 0;
    }
    int main()
    {
        #ifdef WIN32
        freopen("a.in","r",stdin);
        //freopen("c.out","w",stdout);
        #else
        #endif
        N=read();
        S=0,T=N*2|1;
        for(int i=1;i<=N;i++) 
            for(int j=1;j<=N;j++)
                a[i][j]=read();
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
                b[i][j]=read();
        double l=0,r=10000;
        while(r-l>=eps)
        {
            double mid=(l+r)/2;
            if(check(mid)) r=mid;
            else l=mid;
        }
        printf("%.6lf",l);
        return 0;
    }
  • 相关阅读:
    Api记录
    选择排序、冒泡排序、二分查找(折半查找)
    inline关键字
    c++,虚函数,单继承,多继承虚表剖析
    关于类模版迭代器提出时的错误
    关于类模版迭代器提出时的错误2
    菱形继承,多继承,虚继承、虚表的内存结构全面剖析(逆向分析基础)
    VC++错误调试
    引用计数
    选择
  • 原文地址:https://www.cnblogs.com/zwfymqz/p/8503229.html
Copyright © 2011-2022 走看看