2021牛客暑期多校训练营5 - J - Jewels( 二分图带权最大匹配 - KM算法 - O(n^3) DFS版 )

题目链接:点击进入
题目

在这里插入图片描述
在这里插入图片描述

题意

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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值