HDU多校2 - 6767 New Equipments(最小费用最大流)

题目链接:点击查看

题目大意:给出 n 个工人,再给出 m 台机器,第 i 个工人和第 j 台机器匹配的代价是 a[ i ] * j * j + b[ i ] * j + c[ i ] ,问如何进行匹配,可以使得代价和最小

题目分析:代价可以视为二次函数,且数据范围限制了系数 a 是大于 0 的,换句话说,二次函数的开口向上,这样函数就存在着最小值

考虑 n 最大只有 50 ,所以我们可以对于每个工人的二次函数,求出与其匹配所花费代价的前 n 小的位置,求出这些位置之后,直接将工人与机器连边求解就好了,因为每个工人都与 n 个机器建边,所以最终一定可以达到完全匹配,最终有 n 个工人,n * n 台机器,而 spfa 实现的费用流总时间复杂度会退化为 n^5 ,但常数较小,跑起来还是非常快的

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
 
typedef long long LL;
 
typedef unsigned long long ull;
 
const int inf=0x3f3f3f3f;

const int N=3e3+100;//点
 
const int M=5e5+100;//边

LL res[60],a[60],b[60],c[60];

vector<int>node[60];//每个i的前n小的位置 

vector<int>ve;//离散化 
 
struct Edge
{
	int to,w,next;
	LL cost;
}edge[M];
 
int head[N],cnt;
 
void addedge(int u,int v,int w,LL cost)
{
	edge[cnt].to=v;
	edge[cnt].w=w;
	edge[cnt].cost=cost;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	edge[cnt].to=u;
	edge[cnt].w=0;
	edge[cnt].cost=-cost;
	edge[cnt].next=head[v];
	head[v]=cnt++;
}

LL d[N];

int incf[N],pre[N];
 
bool vis[N];
 
bool spfa(int s,int t)
{
	memset(d,0x3f,sizeof(d));
	memset(vis,false,sizeof(vis));
    memset(pre,-1,sizeof(pre));
	queue<int>q;
	q.push(s);
	vis[s]=true;
	incf[s]=inf;
	d[s]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=false;
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			int w=edge[i].w;
			LL cost=edge[i].cost;
			if(!w)
				continue;
			if(d[v]>d[u]+cost)
			{
				d[v]=d[u]+cost;
				pre[v]=i;
				incf[v]=min(incf[u],w);
				if(!vis[v])
				{
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
	return pre[t]!=-1;
}
 
LL update(int s,int t)
{
	int x=t;
	while(x!=s)
	{
		int i=pre[x];
		edge[i].w-=incf[t];
		edge[i^1].w+=incf[t];
		x=edge[i^1].to;
	}
	return d[t]*incf[t];
}
 
void init()
{
	ve.clear();
	for(int i=0;i<60;i++)
		node[i].clear(); 
	memset(head,-1,sizeof(head));
	cnt=0;
}
 
void solve(int st,int ed)
{
	LL ans=0;
	int tot=0;
	while(spfa(st,ed))
	{
		ans+=update(st,ed);
		res[++tot]=ans;
	}
}

LL fun(int i,int x)
{
	return a[i]*x*x+b[i]*x+c[i];
}

void discreate()
{
	sort(ve.begin(),ve.end());
	ve.erase(unique(ve.begin(),ve.end()),ve.end());
}

int get_num(int x)
{
	return lower_bound(ve.begin(),ve.end(),x)-ve.begin()+1;
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int w;
	cin>>w;
	while(w--)
	{
		init();
		int n,m,st=N-1,ed=st-1;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld%lld%lld",a+i,b+i,c+i);
			int pos=-(b[i]/(a[i]*2));//对称轴
			pos=min(pos,m);
			pos=max(pos,1);
			for(int j=pos,cnt=0;j>=1&&cnt<=n;j--,cnt++)//往前找n个 
				node[i].push_back(j);
			for(int j=pos+1,cnt=0;j<=m&&cnt<=n;j++,cnt++)//往后找n个 
				node[i].push_back(j);
			sort(node[i].begin(),node[i].end(),[&](int x,int y)//排序 
			{
				return fun(i,x)<fun(i,y);
			});
			node[i].resize(n);//只保留前n大 
			for(auto it:node[i])
				ve.push_back(it);
		}
		discreate();
		for(int i=1;i<=ve.size();i++)
			addedge(i+n,ed,1,0);
		for(int i=1;i<=n;i++)
		{
			addedge(st,i,1,0);
			for(auto it:node[i])
			{
				int pos=get_num(it);
				addedge(i,pos+n,1,fun(i,it));
			}
		}
		solve(st,ed);
		for(int i=1;i<=n;i++)
			printf("%lld%c",res[i],i==n?'\n':' ');
	}




 














    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/107552228