Loading

2021牛客暑期多校训练营5 J. Jewels(二分图最大权匹配)

链接:https://ac.nowcoder.com/acm/contest/11256/J
来源:牛客网

题目描述

There are nn jewels under the sea, and you want to salvage all the jewels. Image that the sea is a 3D coordinate system, and that you are at (0,0,0)(0,0,0) while the ii-th jewel is at (xi,yi,zi) (zi≥0)(xi,yi,zi) (zi≥0) initially. You can salvage one jewel immediately(regardless of other jewels wherever they are) with strength d2d2 at every non-negative integer moment where d denotes the distance between you and the jewel you salvage at that moment. However, the jewels sink. Specifically, for the ii-th jewel, it will be at (xi,yi,zi+t×vi)(xi,yi,zi+t×vi) after tt seconds. So you want to know the minimum possible total strength to salvage all the jewels.

输入描述:

The first line contains one integer n (1≤n≤300)n (1≤n≤300), denoting the number of jewels.

Following nn lines each contains four integers xi,yi,zi,vi (0≤∣xi∣,∣yi∣,zi,vi≤1000)xi,yi,zi,vi (0≤∣xi∣,∣yi∣,zi,vi≤1000), denoting the jewels' initial positions and sinking velocities.

输出描述:

Output one line containing one integer, denoting the minimum possible total strength to salvage all the jewels.

示例1

输入

复制

3
1 1 1 1
2 2 2 2
3 3 3 3

输出

复制

62

说明

One possible scheme to achive the minimum cost strength:

* At the 0th second, we can salvage the third jewel which is at (3,3,3)(3,3,3) currently with strength 2727.
* At the 1st second, we can salvage the second jewel which is at (2,2,4)(2,2,4) currently with strength 2424.
* At the 2nd second, we can salvage the first jewel which is at (1,1,3)(1,1,3) currently with strength 1111.

So the total strength is 27+24+11=6227+24+11=62.

注意到,如果越迟操作所用的力气就会越大,因此最优策略一定是前n分钟就完成打捞。又因为每分钟只能打捞一件,因此要求的就是一个打捞顺序使得总花费最小。因为一件珠宝在第x分钟的打捞代价容易求,所以可以建完全二分图,左部点为时间,右部点为珠宝,边权为打捞代价。最终要求的就是该二分图的最大权匹配。根据题意,这实际上是一个完美匹配,因此可以采用KM算法。注意蓝书板子的\(O(n^4)\)复杂度难以通过,可以使用优化过的BFS版KM,同时这个题貌似还把最大流卡了。。

以及别忘了long long

#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
typedef long long ll;
typedef unsigned long long ull;//卡精度
using namespace std;
const int N = 507, M = 5e3 +7, maxn = 1007;
const int mod = 1e9+7;
const ll INF = 1e15+7;

ll w[N][N];//边权
ll la[N], lb[N];//左、右部点的顶标
bool va[N], vb[N];//访问标记,是否在交错树中
int match[N];//右部点匹配的左部点(一个只能匹配一个嘛)
int n;
ll delta, upd[N];
int p[N];
ll c[N];

void bfs(int x) {
    int a, y = 0, y1 = 0;

    for(int i = 1; i <= n; ++ i)
        p[i] = 0, c[i] = INF;

    match[y] = x;

    do{
        a = match[y], delta = INF, vb[y] = true;
        for(int b = 1; b <= n; ++ b){
            if(!vb[b]){
                if(c[b] > la[a] + lb[b] - w[a][b])
                    c[b] = la[a] + lb[b] - w[a][b], p[b] = y;
                if(c[b] < delta)//还是取最小的
                    delta = c[b], y1 = b;
            }
        }
        for(int b = 0; b <= n; ++ b)
            if(vb[b])
                la[match[b]] -= delta, lb[b] += delta;
            else c[b] -= delta;
        y = y1;
    }while(match[y]);
    while(y)match[y] = match[p[y]], y = p[y];
}

ll KM() {
    for(int i = 1; i <= n; ++ i)
        match[i] = la[i] = lb[i] = 0;
    for(int i = 1; i <= n; ++ i){
        for(int j = 1; j <= n; ++ j)
            vb[j] = false;
        bfs(i);
    }
    ll res = 0;
    for(int y = 1; y <= n; ++ y)
        res += w[match[y]][y];
    return -res;
}

int v[305], x[305], y[305], z[305];
signed main()
{
    scanf("%lld", &n);
    for(int i = 1; i <= n; ++ i)
        for(int j = 1; j <= n; ++ j)
        w[i][j] = -INF;
    for(int i = 1; i <= n; i++) {
		cin >> x[i] >> y[i] >> z[i] >> v[i];
	}
	for(int i = 1; i <= n; i++) {//时间
		for(int j = 1; j <= n; j++) {//宝藏
			int d = x[j] * x[j] + y[j] * y[j] + ((i - 1) * v[j] + z[j]) * ((i - 1) * v[j] + z[j]);
			w[i][j] = -1ll * d;
			//注意这里不要写w[i][j] = w[j][i] = -d;
		}
	}
    printf("%lld\n", KM());
    return 0;
}

posted @ 2021-08-08 22:18  脂环  阅读(46)  评论(0编辑  收藏  举报