BZOJ1070 最小费用流

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

#include
#include
#include
#include
#include
#include
#include
#include
#include
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(topint 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;
}

你可能感兴趣的:(BZOJ,网络流,费用流)