2021牛客暑期多校训练营5 J Jewels(KM)

J Jewels

题目大意:

矿工捞宝石,宝石在海里,每一宝石都有一个三维坐标 ( x i , y i , z i ) (x_i,y_i,z_i) (xi,yi,zi),且以 v i v_i vi的速度下沉,因此第 i i i秒后的坐标是 ( x i , y i , z i , + ( i − 1 ) ∗ v i ) (x_i,y_i,z_i,+(i-1)*v_i) (xi,yi,zi,+(i1)vi)定义抓取每个宝石的代价为当前位置到原点位置的平方,每次抓宝石都会瞬间移动(每秒一次),问抓取所有的宝石的最小代价

思路:

首先,如果要代价最小,一定要在 n n n秒内抓完所有宝石(这个很好想),我们只需要考虑抓取顺序即可,有涉及代价,所以正解就是费用流拆点,将每个宝石拆为 n n n个点,第 i i i个点代表第 i i i秒钟宝石所对应的代价(位置),然后直接跑费用流即可

建图如下:

在这里插入图片描述

不过,出题人有意卡费用流做法,因此,最后还得用KM来做,把边权改为负,就可以直接跑最大带权匹配了,跑出来的结果是最小带权匹配

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
#define inf 0x7fffffff
#define ll long long
#define int long long
//#define double long double
#define re int
#define void inline void
#define eps 1e-8
//#define mod 1e9+7
#define ls(p) p<<1
#define rs(p) p<<1|1
#define pi acos(-1.0)
#define pb push_back
#define P pair < int , int >
#define mk make_pair
using namespace std;
const int mod=1e9+7;
const int M=1e8+5;
const int N=3e6+5;//?????????? 4e8
int n,m;
int la[N],lb[N],va[N],vb[N];
int match[N],upd[N],pre[N];
int w[1005][1005];
int delta;
void bfs(int x)
{
	int y=0,yy=0;
	for(re i=1;i<=n;i++)  pre[i]=0,upd[i]=1e18;
	match[y]=x;
	do
	{
		int u=match[y],d=1e18;
		vb[y]=1;
		for(re v=1;v<=n;v++)  if(!vb[v])
		{
			if(upd[v]>la[u]+lb[v]-w[u][v])  upd[v]=la[u]+lb[v]-w[u][v],pre[v]=y;
			if(upd[v]<d)  d=upd[v],yy=v;
		}
		for(re v=0;v<=n;v++)  if(vb[v])  la[match[v]]-=d,lb[v]+=d;
		else  upd[v]-=d; 
		y=yy;
	}while(match[y]);
	while(y)  match[y]=match[pre[y]],y=pre[y];
}
int KM()
{
	for(re i=1;i<=n;i++)  match[i]=la[i]=lb[i]=0;
	for(re x=1;x<=n;x++)
	{
		for(re i=1;i<=n;i++)  vb[i]=0;
		bfs(x);
	}
	int ans=0;
	for(re x=1;x<=n;x++)  ans+=w[match[x]][x];
	return ans;
}
int dis(int x,int y,int z)
{
	return x*x+y*y+z*z;
}
int x[N],y[N],z[N],v[N];
void solve()
{
	cin>>n;
	for(re i=1;i<=n;i++)  scanf("%lld%lld%lld%lld",&x[i],&y[i],&z[i],&v[i]);
	for(re i=1;i<=n;i++)  for(re j=1;j<=n;j++)  w[i][j]=-dis(x[j],y[j],z[j]+(i-1)*v[j]);
	cout<<-KM()<<endl;
}
signed main()
{
    int T=1;
//    cin>>T;
    for(int index=1;index<=T;index++)
    {
//    	printf("Case %d:\n",index);
        solve();
//        puts("");
    }
    return 0;
}
/*


1
6 5
0 0 0 122 499 8888




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值