• 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
    ", KM());
        return 0;
    }
    
    
  • 相关阅读:
    Java
    Linux
    Linux
    Linux
    Java
    Ansible
    Ansible
    Java
    ACM&OI 基础数论算法专题
    题解 P4781 【【模板】拉格朗日插值】
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/15116495.html
Copyright © 2020-2023  润新知