题目链接:点击进入
题目
题意
n 个宝石,宝石每个时间的位置都不同,每一个时刻只能挖一颗宝石,挖宝石的代价是三维空间上的距离平方。
思路
n 个时刻 ( [ 0 , n ] ) 对应 n 个宝石,每个时刻只能挖一颗宝石,一边是时间,一边是宝石,像什么呢,像二分图,做什么呢,二分图带权最小匹配,而二分图带权最小匹配就是把边权 w [ i ] [ j ] 取相反数,然后转化为二分图带权最大匹配,最后把 - ans 作为答案输出即可。
模板来自于:lydrainbowcat
代码
//#pragma GCC optimize(3)//O3
//#pragma GCC optimize(2)//O2
#include<iostream>
#include<string>
#include<map>
#include<set>
//#include<unordered_map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<fstream>
#define X first
#define Y second
#define best 131
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int,int>
#define lowbit(x) x & -x
#define inf 0x3f3f3f3f
#define int long long
//#define double long double
//#define rep(i,x,y) for(register int i = x; i <= y;++i)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const int maxn=1e6+10;
const int mod=998244353;
const double eps=1e-9;
const int N=310;
/*--------------------------------------------*/
inline int read()
{
int k = 0, f = 1 ;
char c = getchar() ;
while(!isdigit(c)){if(c == '-') f = -1 ;c = getchar() ;}
while(isdigit(c)) k = (k << 1) + (k << 3) + c - 48 ,c = getchar() ;
return k * f ;
}
/*--------------------------------------------*/
int w[N][N]; // 边权
int la[N],lb[N],upd[N]; // 左、右部点的顶标
bool va[N],vb[N]; // 访问标记:是否在交错树中
int match[N]; // 右部点匹配了哪一个左部点
int last[N]; // 右部点在交错树中的上一个右部点,用于倒推得到交错路
int n;
struct node
{
int x;
int y;
int z;
int v;
}a[N];
bool dfs(int x,int fa)
{
va[x]=1;
for(int y=1;y<=n;y++)
{
if(!vb[y])
{
if(fabs(la[x]+lb[y]-w[x][y])==0)
{ // 相等子图
vb[y]=1;
last[y]=fa;
if(!match[y]||dfs(match[y],y))
{
match[y]=x;
return true;
}
}
else if(upd[y]>la[x]+lb[y]-w[x][y])
{
upd[y]=la[x]+lb[y]-w[x][y];
last[y]=fa;
}
}
}
return false;
}
void KM()
{
for(int i=1;i<=n;i++)
{
la[i]=-INF;
lb[i]=0;
for(int j=1;j<=n;j++)
la[i]=max(la[i],w[i][j]);
}
for(int i=1;i<=n;i++)
{
memset(va,0,sizeof(va));
memset(vb,0,sizeof(vb));
for(int j=1;j<=n;j++) upd[j]=INF;
// 从右部点st匹配的左部点match[st]开始dfs,一开始假设有一条0-i的匹配
int st=0;
match[0]=i;
while(match[st])
{ // 当到达一个非匹配点st时停止
int delta=INF;
if(dfs(match[st],st)) break;
for(int j=1;j<=n;j++)
{
if(!vb[j]&&delta>upd[j])
{
delta=upd[j];
st=j; // 下一次直接从最小边开始DFS
}
}
for(int j=1;j<=n;j++)
{ // 修改顶标
if(va[j]) la[j]-=delta;
if(vb[j]) lb[j]+=delta;
else upd[j]-=delta;
}
vb[st]=true;
}
while(st)
{ // 倒推更新增广路
match[st]=match[last[st]];
st=last[st];
}
}
}
int get(int t,int i)
{
return a[i].x*a[i].x+a[i].y*a[i].y+(a[i].z+a[i].v*t)*(a[i].z+a[i].v*t);
}
signed main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);cout.tie(0);
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z,&a[i].v);
for(int i=1;i<=n;i++)//时间
for(int j=1;j<=n;j++)//宝石
w[i][j]=-get(i-1,j);
KM();
int ans=0;
for(int i=1;i<=n;i++)
ans+=w[match[i]][i];
printf("%lld\n",-ans);
}