题目大意:给定一张有向图,每条边有一个权值,问从1号点到达n号点的最小花费是多少。现在可以使用最多M次魔法,每次可以把当前路过的边的代价cost变成-cost(经过后代价就会变回去,对每条边可使用任意次魔法)。
主要思路:这题的做法是矩阵乘法。我们先用floyed预处理出每对点之间不使用魔法的最短距离还有最多使用一次魔法的最短距离,用邻接矩阵分别存为a、i。我们定义 C = A * B 为 Cij = min{ Aik + Bkj },显然 {a^M}[1][n] 就是答案(其中 i 为单位矩阵)。可以证明 这种乘法是满足结合率的,所以我们可以利用矩阵乘法快速求出 a^M。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 55;
LL g[N][N];
struct Matrix {
LL a[N][N];
int n;
Matrix() { memset(a, 63, sizeof(a)); }
Matrix operator * (const Matrix &b) const {
Matrix c;
c.n = n;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
for (int k = 1; k <= n; ++k)
c.a[i][j] = min(c.a[i][j], a[i][k] + b.a[k][j]);
return c;
}
};
Matrix aa, I;
Matrix find(int t) {
Matrix ans = I, z = aa;
for (; t; t >>= 1, z = z * z)
if (t & 1) ans = ans * z;
return ans;
}
class NegativeGraphDiv1 {
public:
LL findMin(int n, vector <int> from, vector <int> to, vector <int> weight, int charges) {
int m = from.size();
memset(g, 63, sizeof(g));
for (int i = 0; i < m; ++i) {
int x = from[i], y = to[i], z = weight[i];
g[x][y] = min(g[x][y], (LL)z);
}
for (int i = 1; i <= n; ++i)
g[i][i] = 0;
for (int k = 1; k <= n; ++k)
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
aa.n = I.n = n;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
I.a[i][j] = aa.a[i][j] = g[i][j];
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
for (int k = 0; k < m; ++k) {
int x = from[k], y = to[k], z = weight[k];
aa.a[i][j] = min(aa.a[i][j], g[i][x] + g[y][j] - z);
}
Matrix ans = find(charges);
return ans.a[1][n];
}
};