分层图dp & 拆点连边
继续看图论小寄巧的日报,发现之前的分层图咕了,那甚至是4.17昆明之前的事了
决意好好地补一补,然后发现没有什么东西可弄的..
高中的时候还以为是什么高深的东西..
就是说,如果一个点,或者一张图,有K种状态,那么我们可以建K层图,然后连边,记录转移的信息,来维护..
通信线路
给定 个点, 条边的图,定义一条路径的花费为最大的边权
至多可以选择 条边为0,问点 最小的花费
于是我们可以开 层图,然后对于所有边
,给一个第 层的 指向第 层的 边权为0的边,每层图之间正常连边
然后dijkstra的转移改成 ,逝分合理
当然为了省空间,其实可以只设一个dis[k][i]表示到点i用了k条边的花费,然后简单转移一下,因为不难知道点 只能把当前指向的点和上一层的同样点作为选项,所以不需要把图建出来
#include <algorithm>
#include <bitset>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstring>
#include <deque>
#include <fstream>
#include <functional>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define max(a, b) (a > b ? a : b)
#define min(a, b) (a < b ? a : b)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define pi acos(-1)
#define il inline
#define rg register
#define SZ(x) ((int)(x).size())
#define all(x) x.begin(), x.end()
using namespace std;
typedef vector<int> VI;
typedef pair<int, int> PII;
typedef long long ll;
typedef double db;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int MOD = 998244353;
const int mod = 1e9 + 7;
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
const int N = 1010;
int n, m, k;
int dis[N][N];
bool vis[N][N];
vector<pair<int, int>> adj[N];
priority_queue<pair<int, pair<int, int>>> q;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m >> k;
rep(i, 1, n) rep(j, 0, k) dis[i][j] = INF;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
adj[u].pb(mp(v, w));
adj[v].pb(mp(u, w));
}
dis[1][0] = 0;
q.push(mp(0, mp(1, 0)));
while (!q.empty()) {
int i = q.top().se.fi, j = q.top().se.se;
q.pop();
if (vis[i][j] == true)
continue;
vis[i][j] = true;
for (int G = 0; G < adj[i].size(); G++) {
int v = adj[i][G].fi, w = adj[i][G].se;
if (dis[v][j] > max(dis[i][j], w)) {
dis[v][j] = max(dis[i][j], w);
q.push(mp(-dis[v][j], mp(v, j)));
}
if (j < k && dis[v][j + 1] > dis[i][j]) {
dis[v][j + 1] = dis[i][j];
q.push(mp(-dis[v][j + 1], mp(v, j + 1)));
}
}
}
int ans = INT_MAX;
rep(i, 0, k) ans = min(ans, dis[n][i]);
if (ans == INT_MAX)
puts("-1");
else
cout << ans << endl;
return 0;
}
[JLOI2011] 飞行路线
求的最小的最大边权改为最短路,做法也是一样
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m, k;
cin >> n >> m >> k;
int s, t;
cin >> s >> t;
int tot = k * n + n - 1;
vector<vector<pair<int, int>>> adj(k * n + n);
for (int i = 1; i <= m; ++i) {
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
for (int j = 1; j <= k; ++j) {
adj[u + j * n - n].push_back({v + j * n, 0});
adj[v + j * n - n].push_back({u + j * n, 0});
adj[u + j * n].push_back({v + j * n, w});
adj[v + j * n].push_back({u + j * n, w});
}
}
vector<int> dis(tot + 1, INT_MAX);
dis[s] = 0;
set<pair<int, int>> p;
for (int i = 0; i <= tot; ++i)
p.insert({dis[i], i});
for (int i = 0; i <= tot; ++i) {
int u = p.begin()->second;
p.erase(p.begin());
for (auto it : adj[u]) {
int v = it.first, w = it.second;
if (dis[v] > dis[u] + w) {
p.erase({dis[v], v});
dis[v] = dis[u] + w;
p.insert({dis[v], v});
}
}
}
int Min = INT_MAX;
for (int i = t; i <= tot; i += n) {
Min = min(Min, dis[i]);
}
cout << Min << '\n';
return 0;
}
咳咳我就是来水博客的
主要是今天看了那个Legacy的线段树节点连边,突然觉得这个东西好弱智,之前还是那种理解了写不出来的状态。。
还是太菜了
三倍经验