BZOJ1070 最小费用流

30 篇文章 0 订阅
14 篇文章 0 订阅

这道题的思路真是神,万万没想到,给出题人跪了orzorz
我们首先要想明白的是一个人修车对答案的影响是什么,假设一个人是一个技术人员倒数第一个修车,那么它对答案的影响就是修车时间,如果是倒数第二个那么对答案的影响就是2*修车时间,以此类推,所以我们将一个技术人员拆分成m个,代表一个技术人员倒数第几次修车,让这些点分别向所有车连一条容量为一,费用为t[i][j]*k的流,从起点向每一个拆分出来的点连一条容量为1,费用为0的流,每辆车向汇点也连一条这样的流,然后跑一边最小费用流就好了

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<algorithm>
#include<cmath>
using namespace std;
int dis[100000];
struct my_road
{
    int r,f,v;
}a[100000];
int fir[100000];
int nex[100000];
int dui[1000000];
bool pd[100000];
int fro[100000];
int tot=1;
int s,t;
void add_edge(int l,int r,int f,int v)
{
    a[++tot].r=r;
    a[tot].f=f;
    nex[tot]=fir[l];
    fir[l]=tot;
    a[tot].v=v;
}
int cost=0;
int add_flow()
{
    int u=t;
    int flow=2147483647;
    while(u!=s)
    {
        flow=min(flow,a[fro[u]].f);
        u=a[fro[u]^1].r;
    }
    u=t;
    while(u!=s)
    {
        cost+=a[fro[u]].v*flow;
        a[fro[u]].f-=flow;
        a[fro[u]^1].f+=flow;
        u=a[fro[u]^1].r;
    }
    return flow;
}

bool spfa()
{
    int top=1,my_final=2;
    memset(dis,0x1f,sizeof(dis));
    memset(pd,0,sizeof(pd));
    dui[1]=s;
    dis[s]=0;
    pd[s]=true;
    while(top<my_final)
    {
        int u=dui[top];
        for(int o=fir[u];o!=0;o=nex[o])
        {
            if(a[o].f && dis[a[o].r]>dis[u]+a[o].v)
            {
                fro[a[o].r]=o;
                dis[a[o].r]=dis[u]+a[o].v;
                if(!pd[a[o].r]) dui[my_final++]=a[o].r,pd[a[o].r]=true;
            }
        }
        pd[u]=false;
        top++;
    }
    if(dis[t]==0x1f1f1f1f) return false;
    return true;
}
int n,m;
int mid[100][100];
int main()
{
    s=0,t=99999;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
    {
        add_edge(s,i,1,0);
        add_edge(i,s,0,0);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&mid[j][i]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int k=1;k<=n;k++)
            {
                add_edge(i,n+k*m+j,1,mid[j][i]*k);
                add_edge(n+k*m+j,i,0,-mid[j][i]*k);
            }
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            add_edge(n+j*m+i,t,1,0);
            add_edge(t,n+j*m+i,0,0);
        }
    int ans=0;
    while(spfa())
    {
        ans+=add_flow();
    }
    double trueans=cost;
    trueans=trueans/double(n);
    printf("%.2f",trueans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值