链接: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; }