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
#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 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 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