HDU6767 New Equipments(费用流+二分图性质)

HDU6767 New Equipments

题意:

n个工人,m台设备。如果工人被分到第j台设备,则需要支付一个二次函数的费用。询问怎么设计方案使得找到K对工人和设备使得费用最小,输出K是1~n的答案

题解:

根据函数性质,确定工人在1~m里最小的n个点。

0为超级源点

1~n为工人

n+1~n+x为工人们可能会用到的设备

n+x+1为假汇点

n+x+2为超级汇点

源点向每个工人连容量为1,费用为0的边

每个工人向各自前n小的点连容量为1,费用为cal(x)的边

每个设备点向假汇点连容量为1,费用为0的边,表示设备只能用1次。

假汇点向真汇点连一条容量为n,费用为0的边,表示选n次即可。

考虑到二分图的性质,每增广一次后的答案就是当前K对应的答案。

如果对每个K建新图,超时。

如果用Map离散化设备编号,超时。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=55*55+55+10;
int T,n,m;
int M;
const ll inf=1e18;
int visit[maxn];
int s,t;
ll dis[maxn];
int pre[maxn];
int lst[maxn];
ll flow[maxn];
ll maxflow;
ll mincost;

struct node {
    int u,v;
    ll flow;
    ll dis;
    int nxt;
}edge[maxn<<5];
int tot,cnt;
int head[maxn];
void addedge (int u,int v,ll flow,ll dis) {
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].flow=flow;
    edge[tot].dis=dis;
    edge[tot].nxt=head[u];
    head[u]=tot++;
    
    edge[tot].u=v;
    edge[tot].v=u;
    edge[tot].flow=0;
    edge[tot].dis=-dis;
    edge[tot].nxt=head[v];
    head[v]=tot++;
}
bool spfa (int s,int t) {
    for (int i=0;i<=t;i++) {
        dis[i]=inf;flow[i]=inf;visit[i]=0;
    }
    queue<int> q;q.push(s);
    visit[s]=1;
    dis[s]=0;
    pre[t]=-1;
    while (q.size()) {
        int u=q.front();q.pop();
        visit[u]=0;
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            if (edge[i].flow>0&&dis[edge[i].v]>dis[u]+edge[i].dis) {
                dis[edge[i].v]=dis[u]+edge[i].dis;
                pre[edge[i].v]=u;
                lst[edge[i].v]=i;
                flow[edge[i].v]=min(flow[u],edge[i].flow);
                if (!visit[edge[i].v]) {
                    visit[edge[i].v]=1;
                    q.push(edge[i].v);
                }
            }
        } 
    }
    return pre[t]!=-1;
}
void mcmf () {
    maxflow=0;
    mincost=0;
    int num=0;
    while (spfa(s,t)) {
        int u=t;
        maxflow+=flow[t];
        mincost+=flow[t]*dis[t];
        while (u!=s) {
            edge[lst[u]].flow-=flow[t];
            edge[lst[u]^1].flow+=flow[t];
            u=pre[u];
        }
        printf("%lld",mincost);
        num++;
        if (num<n)
            printf(" ");
        else
            printf("\n");
    }
}

ll a[maxn];
ll b[maxn];
ll c[maxn];
ll cal (ll A,ll B,ll C,ll X) {
    return A*X*X+B*X+C;
}
vector<int> g[maxn];//每个点对应的前n小的设备
double A,B,C;
bool cmp (int x,int y) {
    return cal(A,B,C,x)<cal(A,B,C,y);
}
void get (int x) {
    //预处理出点x对应的前n小的点 
    int p=-1.0*b[x]/(a[x]*2);
    if (p<=1) p=1;
    if (p>=m) p=m;
    for (int i=p;i<=min(m,p+n);i++) g[x].push_back(i);
    for (int i=p-1;i>=max(1,p-n);i--) g[x].push_back(i);
    A=a[x];B=b[x];C=c[x];
    sort(g[x].begin(),g[x].end(),cmp);
} 
int lsh[maxn];
int main () {
    scanf("%d",&T);
    while (T--) {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) scanf("%lld%lld%lld",&a[i],&b[i],&c[i]),g[i].clear();
        for (int i=1;i<=n;i++) get(i);
        tot=0;
        for (int i=1;i<=n;i++) for (int j=0;j<min(n,(int)g[i].size());j++) lsh[++tot]=g[i][j];
        sort(lsh+1,lsh+tot+1);
        M=unique(lsh+1,lsh+tot+1)-lsh-1;
        for (int i=0;i<maxn;i++) head[i]=-1;tot=0;
        s=0;
        for (int i=1;i<=n;i++) {
            addedge(s,i,1,0);
            for (int j=0;j<min((int)g[i].size(),n);j++) {
                int p=upper_bound(lsh+1,lsh+M+1,g[i][j])-lsh-1;
                addedge(i,n+p,1,cal(a[i],b[i],c[i],g[i][j]));
            }
        }
        t=n+M+2;
        for (int i=1;i<=M;i++) {
            addedge(n+i,n+M+1,1,0);
        }
        addedge(n+M+1,t,n,0);
        mcmf();
    }
}

 

posted @ 2020-09-22 19:53  zlc0405  阅读(146)  评论(0编辑  收藏  举报