POJ - 3686 The Windy's (最小费用流,主要是建边。)

题目链接

有n个订单m个车间,每个车间均可以单独完成任何一个订单。每个车间完成不同订单的时间是不同的。不会出现两个车间完成同一个订单的情况。给出每个订单在某个车间完成所用的时间。问订单完成的平均时间是多少。加上等待时间。

我们假设一个车间需要完成k个订单,消耗的总时间是t1+(t1+t2)+(t1+t2+t3)……转换一下就是t1*k+t2*(k-1)+t3*(k-3)……

可以想象,全部都在一个车间完成,那么消耗时间少的一定要在第一个完成,这个才能最大化节省时间。,

然后我们怎么建图呢?

假如有 m 个车间, n 个玩具,那么我们把每一个车间分成  n  个车间, 相当于总共 有 n * m 个车间。

每个车间只能生产一个玩具,

1 ~ n 就是1号车间。

然后每个 玩具生产时间 * i  向第 i 个点连一条容量为  1 的边,

依次类推,向所有的车间连边。

最后我们跑最小费用流。就一定可以找到一个最优解。

 

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mem(x,v) memset(x,v,sizeof(x))
const int INF = 0x3f3f3f3f;
const int N = 3e3;
const int M = 3e5;
struct edge{
    int u,v,w,c;
}f[M];
int pre[N],Next[M],head[N],dis[N],flow[N];
int cnt,n,m,s,t,z[60][60];
bool vis[N];
void _Add(int u, int v, int w, int c){
    cnt++;
    Next[cnt] = head[u];
    head[u] = cnt;
    f[cnt].u = u;
    f[cnt].v = v;
    f[cnt].w = w;
    f[cnt].c = c;
    return;
}
void Add_edge(int u, int v, int w, int c ){
    _Add(u,v,w,c);
    _Add(v,u,0,-c);
    return;
}
void init(){
    scanf("%d%d",&n,&m);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf("%d",&z[i][j]);
    s = 0; t = n + n * m + 1;
    cnt = -1;
    mem(head,-1);
    for (int i = 1; i <= n; i++)
        Add_edge(0,i,1,0);
    for (int i = 1; i <= m; i++){
        for (int j = 1; j <= n; j++){
            Add_edge(i * n + j,t,1,0);
            for (int k = 1; k <= n; k++)
                Add_edge(k,i * n + j,1,j * z[k][i]);
        }
    }
}
bool spfa(){
    mem(pre,-1);
    mem(dis,INF);
    mem(flow,0);
    mem(vis,0);
    flow[0] = INF;
    dis[0] = 0;
    queue<int>q;
    while(!q.empty()) q.pop();
    q.push(0);
    vis[0] = 1;
    while(!q.empty()){
        int u = q.front(); q.pop();
        vis[u] = 0;
        for (int i = head[u]; i != -1; i = Next[i]){
            int v = f[i].v;
            if (f[i].w > 0 && dis[v] > dis[u] + f[i].c){
                dis[v] = dis[u] + f[i].c;
                flow[v] = min(flow[u],f[i].w);
                pre[v] = i;
                if (vis[v] == 0){
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    if (pre[t] == -1) return 0;
    return 1;
}
int min_cost_flow(){
    int Ans = 0;
    while(spfa()){
        int now = t;
        int last = pre[t];
        while(now != 0){
            f[last].w -= flow[t];
            f[last ^ 1].w += flow[t];
            now = f[last].u;
            last = pre[now];
        }
        Ans += dis[t];
    }
    return Ans;
}
int main() {
    int T;
    cin>>T;
    while(T--) {
        init();
        int Ans = min_cost_flow();
        printf("%.6lf\n",(Ans*1.0)/n);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值