zoukankan      html  css  js  c++  java
  • [BZOJ4819][SDOI2017]新生舞会(分数规划+费用流,KM)

    4819: [Sdoi2017]新生舞会

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 1097  Solved: 566
    [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

    [Submit][Status][Discuss]

    有点太裸了,两个算法都非常明显。

    ”根据答案的式子可以确定是分数规划,根据题目名称‘舞会’可以确定是二分图匹配”然后这题就做完了。

    先知道是KM,然后看网上写的都是网络流然后也开始写网络流,写了半天发现是费用流。。

    费用流方面并不是普通的最大费用流,因为最后必须全部匹配,所以直接把SPFA成功的条件从一般最大费用流的"dis[T]>0"改成"dis[T]!=-inf"就好了。

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define rep(i,l,r) for (int i=l; i<=r; i++)
     4 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
     5 using namespace std;
     6 
     7 const int N=210,M=30100,inf=1000000000;
     8 const double eps=1e-10;
     9 double ans,c[M],dis[N];
    10 int n,cnt,mn,S,T,f[M],to[M],nxt[M],q[M],pre[N],inq[N],h[N],a[N][N],b[N][N];
    11 void add(int u,int v,int w,double co){
    12     to[++cnt]=v; f[cnt]=w; c[cnt]=co; nxt[cnt]=h[u]; h[u]=cnt;
    13     to[++cnt]=u; f[cnt]=0; c[cnt]=-co; nxt[cnt]=h[v]; h[v]=cnt;
    14 }
    15 
    16 bool spfa(){
    17     rep(i,0,T) pre[i]=-1,inq[i]=0,dis[i]=-inf;
    18     dis[S]=0; q[1]=S;
    19     for (int st=0,ed=1; st<ed; ){
    20         int x=q[++st]; inq[x]=0;
    21         For(i,x) if (f[i] && dis[k=to[i]]<dis[x]+c[i]){
    22             dis[k]=dis[x]+c[i]; pre[k]=i;
    23             if (!inq[k]) inq[k]=1,q[++ed]=k;
    24         }
    25     }
    26     return dis[T]!=dis[0];
    27 }
    28 
    29 void work(){
    30     for (ans=0; spfa(); ans+=dis[T]*mn){
    31         mn=inf;
    32         for (int i=pre[T]; ~i; i=pre[to[i^1]]) mn=min(mn,f[i]);
    33         for (int i=pre[T]; ~i; i=pre[to[i^1]]) f[i]-=mn,f[i^1]+=mn;
    34     }
    35 }
    36 
    37 int main(){
    38     freopen("ball.in","r",stdin);
    39     freopen("ball.out","w",stdout);
    40     scanf("%d",&n);
    41     rep(i,1,n) rep(j,1,n) scanf("%d",&a[i][j]);
    42     rep(i,1,n) rep(j,1,n) scanf("%d",&b[i][j]);
    43     double L=0,R=10000; S=n*2+1,T=n*2+2;
    44     while (L+eps<R){
    45         double mid=(L+R)/2; ans=0;
    46         rep(i,1,2*n+3) h[i]=0; cnt=1;
    47         rep(i,1,n) add(S,i,1,0);
    48         rep(i,1,n) add(i+n,T,1,0);
    49         rep(i,1,n) rep(j,1,n) add(i,j+n,1,a[i][j]-mid*b[i][j]);
    50         work(); if (ans>eps) L=mid; else R=mid;
    51     }
    52     printf("%.6lf
    ",L);
    53     return 0;
    54 }

    KM就没什么好说的了,速度快5倍。果然不能依靠玄学,当然这题的图比较稠密也是原因之一。

    原来KM也可以跑负权图。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define rep(i,l,r) for (int i=l; i<=r; i++)
     5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
     6 using namespace std;
     7 
     8 const int N=210,inf=1000000000;
     9 const double eps=1e-10;
    10 int n,lk[N],vx[N],vy[N],a[N][N],b[N][N];
    11 double lx[N],ly[N],w[N][N],s[N];
    12 double abs(double x){ return (x<0)?-x:x; }
    13 
    14 bool dfs(int x){
    15     vx[x]=1;
    16     rep(y,1,n) if (!vy[y]){
    17         double t=lx[x]+ly[y]-w[x][y];
    18         if (abs(t)<eps){
    19             vy[y]=1;
    20             if (lk[y]==-1 || dfs(lk[y])) { lk[y]=x; return 1; }
    21         }else s[y]=min(s[y],t);
    22     }
    23     return 0;
    24 }
    25 
    26 double KM(){
    27     rep(i,1,n) lx[i]=-inf,ly[i]=0,lk[i]=-1;
    28     rep(i,1,n) rep(j,1,n) lx[i]=max(lx[i],w[i][j]);
    29     rep(x,1,n){
    30         rep(i,1,n) s[i]=inf;
    31         while (1){
    32             memset(vx,0,sizeof(vx));
    33             memset(vy,0,sizeof(vy));
    34             if (dfs(x)) break;
    35             double d=inf;
    36             rep(i,1,n) if (!vy[i]) d=min(d,s[i]);
    37             rep(i,1,n) if (vx[i]) lx[i]-=d;
    38             rep(i,1,n) if (vy[i]) ly[i]+=d; else s[i]-=d;
    39         }
    40     }
    41     double res=0;
    42     rep(i,1,n) res+=w[lk[i]][i];
    43     return res;
    44 }
    45 
    46 int main(){
    47     freopen("ball.in","r",stdin);
    48     freopen("ball.out","w",stdout);
    49     scanf("%d",&n);
    50     rep(i,1,n) rep(j,1,n) scanf("%d",&a[i][j]);
    51     rep(i,1,n) rep(j,1,n) scanf("%d",&b[i][j]);
    52     double L=0,R=10000;
    53     while (L+eps<R){
    54         double mid=(L+R)/2;
    55         rep(i,1,n) rep(j,1,n) w[i][j]=a[i][j]-mid*b[i][j];
    56         if (KM()>eps) L=mid; else R=mid;
    57     }
    58     printf("%.6lf
    ",L);
    59     return 0;
    60 }
  • 相关阅读:
    Windows Server 2012 R2 里面如何安装Net Framework 3.5
    虚拟机网络驱动(共享文件夹)不见了的解决方案-适用于win7~win10 and Windows Server 2008~Windows Server 2012R2
    在计算机 . 上没有找到服务 WAS
    免费获取WP之类的开发者权限或免费使用Azure 2015-10-19
    颠覆你的认知,带你领略史上最为齐全的微软黑科技之旅
    【技巧】只利用 Visual Stdio 自带的工具这么找父类?
    网站定位之---根据IP获得区域
    06.移动先行之谁主沉浮----我的代码我来写(Xaml的优势)
    05.移动先行之谁主沉浮----小应用的美化
    04.移动先行之谁主沉浮----XAML的探索
  • 原文地址:https://www.cnblogs.com/HocRiser/p/8721509.html
Copyright © 2011-2022 走看看