「BZOJ 4681」[JSOI2010]旅行
给出 n 个点 m 条边的无向图,可以交换任意两条边的权值 k 次,求 1 结点到 n 结点的最短路。 \((1 \le N \le 50,1\le M \le 150,1\le K \le20)\)
分析
一开始想忽略每条边的权值,跑一遍最短路,求出边数最少的路径,然后找出这条路径上的边,将这些边从大到小排序,将前 k 大的边与图中所有的边中最小的交换,结果辛辛苦苦调了好久,爆零惹 QAQ 。
然后看题解。
DP + 最短路
把所有的边从小到大排序,那么贪心的做的话,肯定有一个分界线 L ,使得 L 前面的边全部被使用,后面的边都不会被选用,我们枚举这个分界线 L ,设 f[i][j][k] 表示当前是 i 结点,使用了前 L 条边的 j 条,用了 k 次魔法。
于是 DP 时出现了两种情况:
对于当前权值为 w 的边 (u, v)
- 这条边是前 L 条边中的一条
f[v][j + 1][k] = min(f[v][j + 1][k], f[u][j][k] + w)因为这条边和第 j + 1 条边一定会被选用,为了方便枚举,我们从小到大选用。
这条边不是前 L 条边中的一条
f[v][j][k] = min(f[v][j][k], f[u][j][k] + w)直接使用这条边
f[v][j + 1][k + 1] = min(f[v][j + 1][k + 1], f[u][j][k] + w[j + 1])将这条边与第 j + 1 条边交换
代码
#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 55
#define M 155
#define mp make_pair
#define INF 0x3f3f3f3f
#define DEBUG puts("ok")
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
template <typename T> inline void read(T &x) {
T f = 1; x = 0; char c;
for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
x *= f;
}
struct edge {
int d[2], val;
friend bool operator < (edge x, edge y) { return x.val < y.val; }
} e[M];
struct node { int u, j, k; };
int n, m, k, ans;
int f[N][M][N];
bool inq[N][M][N];
queue <node> q;
vector <int> ve[N];
void solve(int L) {
memset(f, INF, sizeof f);
f[1][0][0] = 0;
q.push((node){1, 0, 0});
while (!q.empty()) {
int u = q.front().u, j = q.front().j, kk = q.front().k;
q.pop(); inq[u][j][kk] = 0;
for (int i = ve[u].size() - 1; i >= 0; --i) {
int now = ve[u][i], v = e[now].d[u == e[now].d[0]];
if (now <= L) {
if (j < L && f[v][j + 1][kk] > f[u][j][kk] + e[j + 1].val) {
f[v][j + 1][kk] = f[u][j][kk] + e[j + 1].val;
if (!inq[v][j + 1][kk]) {
q.push((node){v, j + 1, kk});
inq[v][j + 1][kk] = 1;
}
}
}
else {
if (j < L && kk < k && f[v][j + 1][kk + 1] > f[u][j][kk] + e[j + 1].val) {
f[v][j + 1][kk + 1] = f[u][j][kk] + e[j + 1].val;
if (!inq[v][j + 1][kk + 1]) {
q.push((node){v, j + 1, kk + 1});
inq[v][j + 1][kk + 1] = 1;
}
}
if (f[v][j][kk] > f[u][j][kk] + e[now].val) {
f[v][j][kk] = f[u][j][kk] + e[now].val;
if (!inq[v][j][kk]) {
q.push((node){v, j, kk});
inq[v][j][kk] = 1;
}
}
}
}
}
for (int i = 0; i <= k; ++i) ans = min(ans, f[n][L][i]);
}
int main() {
ans = 2147483647;
read(n), read(m), read(k);
for (int i = 1; i <= m; ++i) read(e[i].d[0]), read(e[i].d[1]), read(e[i].val);
sort(e + 1, e + 1 + m);
for (int i = 1; i <= m; ++i) ve[e[i].d[0]].push_back(i), ve[e[i].d[1]].push_back(i);
for (int i = 0; i <= m; ++i) solve(i);
printf("%d", ans);
return 0;
}
顺便挂一下我的 0pts 代码

浙公网安备 33010602011771号