【Luogu】P3705新生舞会(费用流+分数规划+二分答案)

  题目链接

  本来以为自己可以做出来,结果……打脸了

  (貌似来wc立了好几个flag了,都没竖起来)

  不过乱蒙能蒙出一个叫“分数规划”的东西的式子还是很开心的

  观察$C=\frac{a_{1}+a_{2}+.......+a_{n}}{b_{1}+b_{2}+.....b_{n}}$

  然后可以把分母乘到左边

  然后可以把C乘进去

  然后二分C,建图求最大权匹配,判断跟答案的关系。

  

#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
#define maxn 450
#define eps 1e-7
using namespace std;

inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='0')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

struct Edge{
    int from,next,to,val;
    double dis;
}edge[maxn*maxn*3];
int head[maxn],num;
inline void addedge(int from,int to,int val,double dis){
    edge[++num]=(Edge){from,head[from],to,val,dis};
    head[from]=num;
}
inline void add(int from,int to,int val,double dis){
    addedge(from,to,val,dis);
    addedge(to,from,0,-dis);
}
inline void clear(){    memset(head,0,sizeof(head));    num=0;    }
inline int count(int i){    return i&1?i+1:i-1;    }

double dis[maxn];
int pre[maxn];
int flow[maxn];
bool vis[maxn];
int d[maxn][maxn];
int w[maxn][maxn];
int Start,End;
int n;

double spfa(){
    for(int i=Start;i<=End;++i)    dis[i]=-0x7fffffff; dis[Start]=0;
    queue<int>q; q.push(Start);    memset(flow,0,sizeof(flow)); flow[Start]=0x7fffffff;
    while(!q.empty()){
        int from=q.front();    q.pop();    vis[from]=0;
        for(int i=head[from];i;i=edge[i].next){
            int to=edge[i].to;
            if(edge[i].val<=0||dis[to]>=dis[from]+edge[i].dis)    continue;
            dis[to]=dis[from]+edge[i].dis;
            pre[to]=i;    flow[to]=min(flow[from],edge[i].val);
            if(vis[to])    continue;
            vis[to]=1;    q.push(to);
        }
    }
    if(flow[End]==0)    return 0;
    int now=End;
    while(now!=Start){
        //printf("D");
        int i=pre[now];
        edge[i].val-=flow[End];
        edge[count(i)].val+=flow[End];
        now=edge[i].from;
    }
    return dis[End];
}

bool payflow(double lim){
    clear();
    for(int i=1;i<=n;++i){
        add(Start,i,1,0);
        add(i+n,End,1,0);
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)    add(i,j+n,1,1.0*d[i][j]-1.0*w[i][j]*lim);
    double ret=0;
    while(1){
        double now=spfa();
        if(fabs(now)<=eps)    break;
        ret+=now;
    }
    return ret>0;
}

int main(){
    n=read();    End=n*2+1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)    d[i][j]=read();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)    w[i][j]=read();
    double l=0,r=1e4;double ans=0;
    while(r-l>eps){
        double mid=(l+r)/2.0;
        if(payflow(mid)){
            ans=mid;
            l=mid;
        }
        else    r=mid;
    }
    printf("%.6lf",ans);
    return 0;
}

 

posted @ 2018-02-07 11:30  Konoset  阅读(110)  评论(0编辑  收藏  举报