Jewels
题目传送门:
题面:
题目大意:
捞宝石,代价是 x 2 + y 2 + z 2 x^2+y^2+z^2 x2+y2+z2,宝石每时每刻都在下沉变成 ( x , y , z + v t ) (x,y,z+vt) (x,y,z+vt)。问一堆宝石,按照某种最优排列捞使得总代价最小。
思路:
一开始在想贪心,觉得每个时刻钻石增加的代价是
2
v
t
z
+
v
2
t
2
2vtz+v^2t^2
2vtz+v2t2,但是这个式子有多个变量,实际上代价增长与宝石状态与时刻都有关,尝试对总时间贪心/下一时刻贪心均失败。
实际上是以每一个t都对应了一堆宝石的代价,画出来图大概就是t为根,好多代价为叶节点。
所有的宝石肯定都在n 个时刻被挖掉,那么问题就变成一个最小权匹配。一边是时刻,一边是宝石。
边权就是在这个时刻挖这个宝石所消耗的体力值。
二分图匹配
。
跑一个匈牙利算法。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#define ll long long
using namespace std;
const int N = 305;
const ll inf = 1e18;
struct node
{
ll x, y, z, v;
}p[N];
ll n,m,val[N][N],matched[N];
ll slack[N],pre[N],ex[N],ey[N];//ex,ey顶标
bool visx[N],visy[N];
void match(ll u)
{
ll x,y=0,yy=0,delta;
memset(pre,0,sizeof(pre));
for(ll i=1;i<=n;i++)slack[i]=inf;
matched[y]=u;
while(1)
{
x=matched[y];delta=inf;visy[y]=1;
for(ll i=1;i<=n;i++)
{
if(visy[i])continue;
if(slack[i]>ex[x]+ey[i]-val[x][i])
{
slack[i]=ex[x]+ey[i]-val[x][i];
pre[i]=y;
}
if(slack[i]<delta){delta=slack[i];yy=i;}
}
for(ll i=0;i<=n;i++)
{
if(visy[i])ex[matched[i]]-=delta,ey[i]+=delta;
else slack[i]-=delta;
}
y=yy;
if(matched[y]==-1)break;
}
while(y){matched[y]=matched[pre[y]];y=pre[y];}
}
ll KM()
{
memset(matched,-1,sizeof(matched));
memset(ex,0,sizeof(ex));
memset(ey,0,sizeof(ey));
for(ll i=1;i<=n;i++)
{
memset(visy,0,sizeof(visy));
match(i);
}
ll res=0;
for(ll i=1;i<=n;i++)
if(matched[i]!=-1)res+=val[matched[i]][i];
return res;
}
int main()
{
scanf("%lld",&n);
for (ll i = 1; i <=n; ++i){
ll x, y, z, v;
scanf("%lld%lld%lld%lld", &x, &y, &z, &v);
for (ll j = 1; j <=n; ++j)
val[i][j]=-(x*x+y*y+(z+(j-1)*v)*(z+(j-1)*v));
}
printf("%lld", -KM());
return 0;
}