2021暑假牛客5_J.Jewels

本文介绍了一个关于宝石捞取的算法问题,通过分析宝石随时间变化的成本,将其转化为一个二分图匹配问题,并使用匈牙利算法求解最小权匹配,最终实现最小化总成本的目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值