题解:P6029 [JSOI2010] 旅行
five_rice_water · · 题解
洛谷P6029 题解
本人过的第一道黑题,也是本人在洛谷的第
这题其实不是很难,个人感觉到不了黑。
题意简述
对于一个有
思路
我们先来考虑和本题的思路部分很像的这样一道题:
现在有一个序列
不难发现,当
一种相对可行的解法是这样的,我们枚举最终最大子段和所在的区间左右端点,再去考虑往这个区间里面交换前
当然要考虑前
总结一下,我们用到的思路是枚举终态,也就是考虑最终的答案可能来自哪些部分,然后进一步操作。
再简单一点,就是找一些能概括所有状态的情况,然后依次枚举所有情况,找到在该种情况的最优解,最后得到最终的最优解。
那么放到这道题上,到底哪些状态是有限可以被枚举同时可以概括所有情况的呢?
一种可行的情况是这张图上前
这样枚举,可以概括所有的情况。
代码实现
第一步,我们要枚举
但是我们已经知道了前
我们可以用 dp 去获得“前
怎么定义状态呢?我们设
怎么转移呢?有以下两种情况。
当
另外,当这条边是我们枚举的前
否则,这条边要计算到答案里,更新
最后,从
最后加上
最后的答案就是所有
代码展示
代码实现时注意一下边界处理,例如下面的 j+1<=m。
#include <bits/stdc++.h>
using namespace std;
const int N = 55;
const int M = 155;
int n, m, k;
struct graph {
int v, w, id;
};
vector<graph>e[N];
struct node {
int x, j, k, dis;
friend bool operator <(node a, node b) {
return a.dis > b.dis;
}
};
struct edge {
int w, id;
} E[M];
bool cmp(edge a, edge b) {
return a.w < b.w;
}
int mp[M], f[N][M][M], vis[N][M][M];
void dijkstra(int check) {
memset(f, 0x3f, sizeof(f));
memset(vis, 0, sizeof(vis));
f[1][0][0] = 0;
priority_queue<node>q;
q.push({1, 0, 0, 0});
while (!q.empty()) {
node tmp = q.top();
q.pop();
int x = tmp.x;
int j = tmp.j;
int k_ = tmp.k;
if (vis[x][j][k_])
continue;
vis[x][j][k_] = 1;
for (int i = 0; i < e[x].size(); i++) {
int v = e[x][i].v;
int w = e[x][i].w;
int id = e[x][i].id;
if (k_ + 1 <= k && f[v][j][k_ + 1] > f[x][j][k_]) {
f[v][j][k_ + 1] = f[x][j][k_];
q.push({v, j, k_ + 1, f[v][j][k_ + 1]});
}
if (mp[id] <= check) {
if (j + 1 <= m && f[v][j + 1][k_] > f[x][j][k_]) {
f[v][j + 1][k_] = f[x][j][k_];
q.push({v, j + 1, k_, f[v][j + 1][k_]});
}
} else {
if (f[v][j][k_] > f[x][j][k_] + w) {
f[v][j][k_] = f[x][j][k_] + w;
q.push({v, j, k_, f[v][j][k_]});
}
}
}
}
}
int main() {
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) {
int x, y, z;
cin >> x >> y >> z;
e[x].push_back({y, z, i});
e[y].push_back({x, z, i});
E[i] = {z, i};
}
sort(E + 1, E + 1 + m, cmp);
for (int i = 1; i <= m; i++) {
mp[E[i].id] = i;
}
int sum = 0;
int ans = INT_MAX;
for (int i = 0; i <= m; i++) {
sum += E[i].w;
dijkstra(i);
for (int j = 0; j <= i; j++) {
for (int K = 0; K <= k; K++) {
if (j + K <= i) {
ans = min(ans, f[n][j][K] + sum);
} else
break;
}
}
}
cout << ans << endl;
return 0;
}