原题链接
在岸上捕捞n(<=300)个宝物,一秒捕一个,捕捞消耗的体力为宝物的此时坐标(xi,yi,zi+t*vi)的平方,t为当前时间,求捕捞所有宝物的最小消耗体力量。
分析:
易知为二分图的带权最小匹配问题,事实上只要带有“选择”的意味的题目,都可以考虑使用二分图算法试解。
这题的难点主要在卡时间,网上的很多板子都卡了,这里给出一份跑得飞快的二分图的带权匹配的板子。
LL w[maxv][maxv]; //记录边权
bool getVal[maxv][maxv]; //记录该边是否被赋值,以上两个数组都需要在读边的时候完成赋值
namespace KM {
long long cal(int n, int m,bool isSmall) { //带权匹配模板,总n个点,最大匹配m个点,isSmall表示是否是最小带权匹配
std::vector<long long> u(n + 1), v(m + 1), p(m + 1), way(m + 1);
if(!isSmall) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) w[i][j]=-w[i][j];
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(!getVal[i][j]) w[i][j]=1e18;
for (int i = 1; i <= n; i++) {
p[0] = i;
long long j0 = 0;
std::vector<long long> minv(m + 1, 1e18);
std::vector<char> used(m + 1, false);
do {
used[j0] = true;
long long i0 = p[j0], delta = 1e18, j1;
for (int j = 1; j <= m; ++j) {
if (!used[j]) {
long long cur = w[i0][j] - u[i0] - v[j];
if (cur < minv[j]) {
minv[j] = cur, way[j] = j0;
}
if (minv[j] < delta) {
delta = minv[j], j1 = j;
}
}
}
for (int j = 0; j <= m; ++j) {
if (used[j]) {
u[p[j]] += delta, v[j] -= delta;
}
else {
minv[j] -= delta;
}
}
j0 = j1;
} while (p[j0] != 0);
do {
long long j1 = way[j0];
p[j0] = p[j1];
j0 = j1;
} while (j0);
}
long long res = 0;
for (int i = 1; i <= m; i++) {
res += w[p[i]][i];
}
if(!isSmall) res=-res;
return res;
}
}