题意:
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(" "); } } 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(); } }