New Equipments( 最小费用最大流+性质 )

New Equipments( 最小费用最大流+性质 )

hdu6767

题意:n个工人( <=50 ) m台机器( <=1e8 ) ,选择k对工人和机器匹配( 每个人和每个机器都只能用一次 ),第 i 个工人使用第 j 太机器的费用是 ai × j^2 + bi × j + ci , 每个工人都有自己的abc,答案输出n个数,当k=1,2,3...n 是最小费用分别是多少。

思路:很裸的一道最小费用最大流,但是需要优化!1e8台机器肯定不能全加进去,我们只加入对于每个工人来说花费最小的n台机器,最小的n台可以通过给的函数来确定,已知向上开口,无实根或0。

图做好了,一共又n^2个点,跑一次是可以的。但是题目要求跑n次,每次跑都需要重新连边肯定超时了。这就需要了解最小费用最大流的一点性质。代码的每一次spfa()都是找到一条最小费用的增广路,因为我们的图比较特殊( 其实大部分题都是这种样式的图 ),每个工人的流量都是1,使用每个增广路的大小一定是1,所以在MCMF()中的每一次spfa,maxflow增加一,就是说工人增加一,这样就只需要跑一遍n个工人的[1,n]的就都出来了。

代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long

using namespace std;

const int maxn = 55*55*5;
struct node {
    int to,w,f,nxt;
}e[maxn];
int n,s,t,m,maxflow,mincost,ss,tt;
int dis[maxn],flow[maxn],via[maxn],pre[maxn],last[maxn];
int head[maxn],cnt=0;

void addage( int u, int v, int f, int w )
{
//    cout << u << "->" << v << endl;
    e[cnt].f = f;
    e[cnt].w = w;
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt++;
}

int spfa()
{
    memset(dis,inf,sizeof(dis));
    memset(flow,inf,sizeof(flow));
    memset(via,0,sizeof(via));
    queue<int> Q;
    Q.push(s);via[s]=1;dis[s]=0;pre[t]=-1;
    while ( !Q.empty() ) {
        int x = Q.front();Q.pop();via[x]=0;
        for ( int i=head[x]; i!=-1; i=e[i].nxt ) {
            int y = e[i].to,f=e[i].f,w=e[i].w;
            if ( f && dis[y]>dis[x]+w ) {
                dis[y] = dis[x]+w;
                pre[y] = x;
                last[y] = i;
                flow[y] = min(flow[x],f);
                if ( via[y]==0 ) {
                    Q.push(y);
                    via[y] = 1;
                }
            }
        }
    }
    return pre[t]!=-1;
}

void MCMF()
{
    maxflow = mincost = 0;
    int isp = 0;
    while ( spfa() ) {
        int x = t;
        maxflow += flow[t];
        mincost += flow[t]*dis[t];
        while ( x!=s ) {
            e[last[x]].f -= flow[t];
            e[last[x]^1].f += flow[t];
            x = pre[x];
        }
//        cout << maxflow << "-->" << mincost << endl;;
        if ( isp==0 ) {
            cout << mincost ;isp=1;
        }
        else cout << " " << mincost;
//        cout << "maxflow = " << maxflow << "  mincost = " << mincost << endl;
    }
    cout << endl;
}

struct nod {
    int a,b,c;
}a[55];
int b[maxn],pos;
vector<int> link[55];

signed main()
{
//    freopen("1005.in","r",stdin);
//    freopen("ans.out","w",stdout);
    int T;cin>>T;
    while ( T-- ) {
        memset(head,-1,sizeof(head));cnt=0;pos=0;
        for ( int i=1; i<=n; i++ ) link[i].clear();
        cin>>n>>m;
        for ( int i=1; i<=n; i++ ) {
            scanf("%lld %lld %lld",&a[i].a,&a[i].b,&a[i].c);
            int x = (int)(-1.0*a[i].b/(2*a[i].a));
            int want = n+4;
            if ( x<1 ) {
                for ( int j=1; j<=m&&want>0; j++,want-- ) {
                    link[i].push_back(j);
                    b[pos++] = j;
                }
            }
            else if ( x>m ) {
                for ( int j=m; j>=1&&want>0; j--,want-- ) {
                    link[i].push_back(j);
                    b[pos++] = j;
                }
            }
            else {
                int lastj=0,lastj2=0;
                for ( int j=x; j<=m&&want>=n/2+2; j++,want-- ) {
                    link[i].push_back(j);
                    b[pos++] = j;
                    lastj = j;
                }
                for ( int j=x-1; j>=1&&want>0; j--,want-- ) {
                    link[i].push_back(j);
                    b[pos++] = j;
                    lastj2 = j;
                }
                if ( want>0 ) {
                    for ( int j=lastj+1; j<=m&&want>0; j++,want-- ) {
                        link[i].push_back(j);
                        b[pos++] = j;
                    }
                    for ( int j=lastj2-1; j>=1&&want>0; j--,want-- ) {
                        link[i].push_back(j);
                        b[pos++] = j;
                    }
                }
            }
        }
        sort(b,b+pos);
        pos = unique(b,b+pos)-b;
        s = n + pos + 10; t=n + pos + 11;
        ss = n + pos + 12;
        addage( s,ss,n,0 );addage( ss,s,0,0 );
//        cout << n << endl;
        for ( int i=1; i<=n; i++ ) {
//            cout << "now i = " << i << "  now link[i].size = " << link[i].size() << endl;
            addage(ss,i,1,0); addage(i,ss,0,0);
            for ( int j=0; j<link[i].size(); j++ ) {
                int date = link[i][j];
                int x = lower_bound(b,b+pos,date)-b+n+2;
                addage(i,x,1,a[i].a*date*date+a[i].b*date+a[i].c);
                addage(x,i,0,-(a[i].a*date*date+a[i].b*date+a[i].c));
            }
        }
        for ( int i=0; i<pos; i++ ) {
            addage(i+n+2,t,1,0);addage(t,i+n+2,0,0);
        }
        MCMF();
    }

    return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值