「SDOI 2017」新生舞会

题目链接

戳我

\(Describe\)

有一场舞会,n个男生,n个女生,要组成n对舞伴,男生i和女生j组队的适合度是\(a_{ij}\),
不适合度是\(b_{ij}\),
让你求\(max(\sum(适合度)/\sum(不适合度))\)

\(Solution\)

这道题是\(01\)分数规划的好题目。我们首先拆分这个式子:
\(A_i\)为舞伴的适合度,\(B_i\)为不适合度

\[C=\sum_{i=1}^{i<=n}a_i/\sum_{i=1}^{i<=n}b_i\]
\[\sum_{i=1}^{i<=n}a_i=\sum_{i=1}^{i<=n}b_i*C\]
\[\sum_{i=1}^{i<=n}a_i-\sum_{i=1}^{i<=n}b_i*C=0\]

则我们可以对C二分一个数\(mid\)
\[令:ans=max(\sum_{i=1}^{i<=n}a_i-\sum_{i=1}^{i<=n}b_i*mid)\]

如果\(ans>0\)\(ans\)\(C\)要小,反之比\(C\)

那么这个\(max(ans)怎么求呢?\)
这个随便用个费用流搞一搞就好了;

  • 把每个人拆成\(x\)\(x'\)
  • \(S\)\(x\)相连,流量为1,费用为0
  • \(x'\)\(T\)相连,流量为1,费用为0
  • 对于两个人\(x,y\)我们将\(x\)\(y'\)相连,流量为1,费用为\(a[x][y]-mid*b[x][y]\)(因为我的spfa是跑的最短路,所以我写的是-a[x][y]+mid*b[x][y],最后跑出来的答案的绝对值就是最大费用最大流);
  • 跑一遍最大费用最大流

\(Code\)

#include<bits/stdc++.h>
using namespace std;
const double inf=1e9+7;
typedef long long ll;
int read(){
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9')
        f=(c=='-')?-1:1,c=getchar();
    while(c>='0'&&c<='9')
        x=x*10+c-'0',c=getchar();
    return x*f;
}
struct node{
    int to,next,v;
    double w;
}a[1000001];
int f[10001],pre[10001],fa[10001],s,t=1000,head[10001],cnt;
double dis[10001];
void add(int x,int y,int c,double v){
    a[++cnt].to=y,a[cnt].next=head[x],a[cnt].v=c,a[cnt].w=v,head[x]=cnt;
    a[++cnt].to=x,a[cnt].next=head[y],a[cnt].v=0,a[cnt].w=-v,head[y]=cnt;
}
queue<int>q;
int spfa(){
    q.push(s);
    for(int i=s;i<=t;i++)
        dis[i]=inf;
    memset(f,0,sizeof(f));
    f[s]=1,dis[s]=0;
    while(!q.empty()){
        int now=q.front();
        q.pop();
        f[now]=0;
        for(int i=head[now];i;i=a[i].next){
            int v=a[i].to;
            if(dis[v]>dis[now]+a[i].w&&a[i].v){
                dis[v]=dis[now]+a[i].w,pre[v]=i,fa[v]=now;
                if(!f[v])
                    f[v]=1,q.push(v);
            }
        }
    }
    if(dis[t]!=inf)
        return 1;
    return 0;
}
double ans1,ans;
void anser(){
    ans=0,ans1=0;
    while(spfa()){
        int minx=2147483647;
        for(int i=t;i!=s;i=fa[i])
            minx=min(minx,a[pre[i]].v);
        ans+=minx,ans1+=dis[t]*minx;
        for(int i=t;i!=s;i=fa[i])
            a[pre[i]].v-=minx,(pre[i]%2)?a[pre[i]+1].v+=minx:a[pre[i]-1].v+=minx;
    }
}
double A[1001][1001],B[1001][1001];
int n;
bool check(double x){
    memset(head,0,sizeof(head)),cnt=0;
    for(int i=1;i<=n;i++)
        add(s,i,1,0),add(i+n,t,1,0);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            add(i,j+n,1,-A[i][j]+x*B[i][j]);
        }
    anser();
    return (-ans1)>=0;
}
int main(){
    n=read();
    double l=0,r=10000,maxx=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>A[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>B[i][j];
    while(r-l>=1e-7){
        double mid=(l+r)*0.5;
        if(check(mid))
            l=mid,maxx=max(maxx,mid);
        else r=mid;
    }
    printf("%0.6lf",maxx);
}

转载于:https://www.cnblogs.com/hbxblog/p/10266038.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MT的森林舞会是一种编程语言,它的主要目的是帮助孩子们学习和理解编程的概念。它以一种图形化的方式呈现,通过拖动和连接不同的积木块来创建程序。MT的森林舞会为孩子们提供了一个有趣而富有想象力的环境,让他们能够探索和实践编程的基本原理。 MT的森林舞会的编程语言采用了简单明了的命令,例如“移动”、“跳跃”和“旋转”等。通过选择并组合这些命令,孩子们可以编写程序来指导角色在虚拟的森林舞会中进行各种活动。这些活动包括与其他角色的互动、收集物品和解决谜题等。 这种图形化的编程语言不仅使编程变得更易于理解和学习,还激发了孩子们的创造力和逻辑思维能力。他们可以根据自己的想法设计和探索不同的舞会场景,并通过调整命令的顺序和参数来改变角色的行为。 MT的森林舞会还提供了一个可视化的调试工具,帮助孩子们找出程序中的错误并进行修改。这样,他们能够学会分析问题、寻找解决方案,并进行自我纠错。这种及时的反馈和强调实践的学习方法,有助于培养孩子们的解决问题和团队合作的能力。 总之,MT的森林舞会的编程是一种以图形化方式呈现的编程语言,旨在引导孩子们学习和实践编程的基本原理。它通过创造有趣的森林舞会场景,培养孩子们的创造力、逻辑思维和解决问题的能力,为他们打开了编程的大门。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值