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