5.19
https://www.luogu.com.cn/problem/P5960
- 问题转化:将每个差分约束条件$ (x_i - x_j \leq c_k) $转化为图中的一条边 \((j \rightarrow i)\),权值为 \((c_k)\)。
- 虚拟节点:为了确保图是连通的,添加一个虚拟节点 0,并从该节点向所有其他节点连一条权值为 0 的边。
- SPFA 算法:使用 SPFA算法检测负环。如果某个节点的入队次数超过节点总数,则存在负环。有负环即不可差分。
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
public class Main implements Runnable {
static Scanner cin = new Scanner(System.in);
static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
static int n, m;
static final int N = (int) (5e3 + 10);
static Vector<Verge>[] nodes = new Vector[N];
static Queue<Integer> queue = new LinkedList<>();
static boolean[] visited = new boolean[N];
static int[] dist = new int[N];
static int[] cnt = new int[N];
public static int nextInt() {
return cin.nextInt();
}
public static double nextDouble() {
return cin.nextDouble();
}
public static void main(String[] args) {
new Thread(null, new Main(), "", 1 << 20).start();
}
@Override
public void run() {
n = nextInt(); m = nextInt();
for (int i = 0; i <= n; i++) {
nodes[i] = new Vector<>();
}
for (int i = 1; i <= m; i++) {
int to = nextInt(); int from = nextInt(); int val = nextInt();
nodes[from].add(new Verge(to, val));
}
for (int i = 1; i <= n; i++) {
nodes[0].add(new Verge(i, 0));
}
Arrays.fill(dist, (int) 1e9);
if (spfa()) {
cout.println("NO");
} else {
for (int i = 1; i <= n; i++) {
cout.print(dist[i] + " ");
}
cout.println();
}
cout.flush();
}
public static boolean spfa() {
queue.add(0); visited[0] = true; dist[0] = 0;
while (!queue.isEmpty()) {
int from = queue.remove(); visited[from] = false;
for (Verge verge : nodes[from]) {
int to = verge.to; int cost = verge.val;
if (dist[to] > dist[from] + cost) {
dist[to] = dist[from] + cost;
cnt[to] = cnt[from] + 1;
if (cnt[to] >= n + 1) return true;
if (!visited[to]) {
queue.add(to); visited[to] = true;
}
}
}
}
return false;
}
static class Verge {
int to;
int val;
Verge(int to, int val) {
this.to = to;
this.val = val;
}
}
}
[P4568 JLOI2011] 飞行路线 - 洛谷
本题是典型的分层图最短路问题,核心是通过分层处理最多免费乘坐 k 次航线的约束。每层代表使用免费次数的状态,层间转移表示使用一次免费机会,层内转移表示正常付费。
- 分层图构建:
- 创建 (k+1) 层图,每层包含 n 个节点(对应原问题的 n 个城市)。
- 第 i 层\((0 \leq i \leq k)\)表示已使用 i 次免费机会的状态。
- 层内边:同层内的边权为航线原价(需付费)。
- 层间边:从第 i 层的节点 u 到第 (i+1) 层的节点 v 连一条权值为 0 的边(表示使用一次免费机会,费用为 0)。
- Dijkstra 算法求解:
- 起点为第 0 层的起点城市 s,终点为第 0 层到第 k 层的终点城市 t 的最小值,即\(t + k * n\)
- 使用优先队列优化的 Dijkstra 算法,在分层图中寻找从起点到各层终点的最短路径。
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 1e9;
const int N = 110010;
const int M = 50010;
struct Verge {
int to;
int cost;
Verge(int t, int c) : to(t), cost(c) {}
// 重载小于运算符,用于优先队列的比较
bool operator>(const Verge& other) const {
return cost > other.cost;
}
};
int n, m, k;
int s, t;
vector<Verge> nodes[N];
int dist[N];
bool visited[N];
int main() {
// 读取输入
cin >> n >> m >> k >> s >> t;
// 初始化邻接表
for (int i = 0; i <= n * (k + 1) - 1; i++) {
nodes[i].clear();
}
// 构建图
for (int i = 1; i <= m; i++) {
int from, to, cost;
cin >> from >> to >> cost;
// 普通边
nodes[from].push_back(Verge(to, cost));
nodes[to].push_back(Verge(from, cost));
// 分层图的边
for (int j = 1; j <= k; j++) {
// 层内边
nodes[j * n + from].push_back(Verge(j * n + to, cost));
nodes[j * n + to].push_back(Verge(j * n + from, cost));
// 层间边(免费边)
nodes[(j - 1) * n + from].push_back(Verge(j * n + to, 0));
nodes[(j - 1) * n + to].push_back(Verge(j * n + from, 0));
}
}
// Dijkstra算法
fill(dist, dist + N, INF);
dist[s] = 0;
// 使用优先队列优化的Dijkstra
priority_queue<Verge, vector<Verge>, greater<Verge>> queue;
queue.push(Verge(s, 0));
while (!queue.empty()) {
Verge current = queue.top();
queue.pop();
int from = current.to;
int cost = current.cost;
// 原Java代码中的终止条件可能有误,这里注释掉
// if ((from + 1) % n == 0) break;
if (visited[from]) continue;
visited[from] = true;
for (const Verge& verge : nodes[from]) {
int to = verge.to;
int nowCost = verge.cost;
if (visited[to]) continue;
if (dist[to] > nowCost + dist[from]) {
dist[to] = nowCost + dist[from];
queue.push(Verge(to, dist[to]));
}
}
}
// 计算结果
int ans = INF;
for (int i = 1; i <= k + 1; i++) {
ans = min(ans, dist[t + (i - 1) * n]);
}
cout << ans << endl;
return 0;
}
[P2865 USACO06NOV] Roadblocks G - 洛谷
- 双向最短路径预处理:
- 使用 SPFA 算法分别计算从起点 1 到所有节点的最短距离
dist1[],以及从终点 n 到所有节点的最短距离dist2[]。 - 这一步将原图的次短路径问题转化为:通过某条边 (u, v, w) 连接两段最短路径,即 \((dist1[u] + w + dist2[v])\) 或 \((dist1[v] + w + dist2[u])\)。
- 使用 SPFA 算法分别计算从起点 1 到所有节点的最短距离
- 枚举边计算候选次短路径:
- 遍历所有边 (u, v, w),计算通过该边连接起点到 u、终点到 v 的最短路径长度,以及起点到 v、终点到 u 的最短路径长度,得到所有可能的候选值。
- 这些候选值中,严格大于最短路径的最小值即为次短路径。
- 筛选与去重:
- 使用集合存储所有候选值,自动去重后,通过优先队列(或排序)找到严格大于最短路径的最小值。
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
public class Main implements Runnable {
static Scanner cin = new Scanner(System.in);
static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
static int r, n;
static final int N = (int) (5e3 + 10);
static final int R = (int) (1e5 + 10);
static Vector<Verge>[] nodes = new Vector[N];
static int[] dist1 = new int[N];
static int[] dist2 = new int[N];
static boolean[] visited = new boolean[N];
static Queue<Integer> queue = new LinkedList<>();
static Set<Integer> ans = new HashSet<>();
static Queue<Integer> queue2 = new PriorityQueue<>();
public static int nextInt() {
return cin.nextInt();
}
public static double nextDouble() {
return cin.nextDouble();
}
public static void main(String[] args) {
new Thread(null, new Main(), "", 1 << 20).start();
}
@Override
public void run() {
n = nextInt(); r = nextInt();
for (int i = 1; i <= n; i++) {
nodes[i] = new Vector<Verge>();
}
for (int i = 1; i <= r; i++) {
int from = nextInt(); int to = nextInt(); int cost = nextInt();
nodes[from].add(new Verge(to, cost));
nodes[to].add(new Verge(from, cost));
}
Arrays.fill(dist1, (int) 1e9);
spfa1();
queue.clear();
Arrays.fill(visited, false);
Arrays.fill(dist2, (int) 1e9);
spfa2();
for (int from = 1; from <= n; from++) {
for (Verge verge : nodes[from]) {
int to = verge.to; int cost = verge.val;
ans.add(dist1[from] + dist2[to] + cost);
}
}
queue2.addAll(ans);
queue2.poll();
cout.println(queue2.poll());
cout.flush();
}
public static void spfa1() {
queue.add(1); visited[1] = true; dist1[1] = 0;
while (!queue.isEmpty()) {
Integer from = queue.poll(); visited[from] = false;
for (Verge verge : nodes[from]) {
int to = verge.to; int cost = verge.val;
if (dist1[to] > dist1[from] + cost) {
dist1[to] = dist1[from] + cost;
if (!visited[to]) {
queue.add(to); visited[to] = true;
}
}
}
}
}
public static void spfa2() {
queue.add(n); visited[n] = true; dist2[n] = 0;
while (!queue.isEmpty()) {
Integer from = queue.poll(); visited[from] = false;
for (Verge verge : nodes[from]) {
int to = verge.to; int cost = verge.val;
if (dist2[to] > dist2[from] + cost) {
dist2[to] = dist2[from] + cost;
if (!visited[to]) {
queue.add(to); visited[to] = true;
}
}
}
}
}
static class Verge {
int to;
int val;
Verge(int to, int val) {
this.to = to;
this.val = val;
}
}
}
[P2910 USACO08OPEN] Clear And Present Danger S - 洛谷
- 纯floyd的板子题
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
public class Main implements Runnable {
static Scanner cin = new Scanner(System.in);
static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
static final int N = (int) (1e2 + 10);
static final int M = (int) (1e4 + 10);
static int[] a = new int[M];
static int n, m;
static int[][] dist = new int[N][N];
public static int nextInt() {
return cin.nextInt();
}
public static double nextDouble() {
return cin.nextDouble();
}
public static void main(String[] args) {
new Thread(null, new Main(), "", 1 << 20).start();
}
@Override
public void run() {
n = nextInt(); m = nextInt();
for (int i = 1; i <= m; i++) {
a[i] = nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dist[i][j] = nextInt();
}
}
floyd();
int ans = 0;
for (int i = 1; i < m; i++) {
ans += dist[a[i]][a[i + 1]];
}
cout.println(ans);
cout.flush();
}
public static void floyd() {
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
}
}
[P1095 NOIP 2007 普及组] 守望者的逃离 - 洛谷
- 线性dp分类讨论,使用技能一定比不使用技能好,所以先跑一遍使用技能可能的dp值,再对不使用技能的一起dp
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
public class Main implements Runnable {
static Scanner cin = new Scanner(System.in);
static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
static final int v = 17;//17m/s
static final int SkillDistance = 60; //一次技能跑60m 消耗10点魔法
static final int Recovery = 4;
static int m, s, t;
static final int T = (int) (3e5 + 10);
static final int M = (int) (1e3 + 10);
static int[] dp = new int[T];
public static int nextInt() {
return cin.nextInt();
}
public static double nextDouble() {
return cin.nextDouble();
}
public static void main(String[] args) {
new Thread(null, new Main(), "", 1 << 20).start();
}
@Override
public void run() {
m = cin.nextInt(); s = cin.nextInt(); t = cin.nextInt();
for (int i = 0; i < t; i++) {
dp[i + 1] = dp[i] + ((m >= 10) ? 1 : 0) * SkillDistance;
int p = m;
m -= ((p >= 10) ? 1 : 0) * 10;
m += ((p < 10) ? 1 : 0) * Recovery;
}
int ptr = 0;
for (int i = 0; i < t; i++) {
dp[i + 1] = Math.max(dp[i + 1], dp[i] + 17);
if (dp[i + 1] >= s) {
ptr = i + 1;
break;
}
}
if (ptr != 0) {
cout.println("Yes");
cout.println(ptr);
} else {
cout.println("No");
cout.println(dp[t]);
}
cout.flush();
}
}
[P1077 NOIP 2012 普及组] 摆花 - 洛谷
- \(dp[i][j]\)表示前i种花的前j盆有多少可能,为计数类dp,采用累加。因为\(dp[i][0]\)均为1种可能,需要初始化。本题采用正推。
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
public class Main implements Runnable {
static Scanner cin = new Scanner(System.in);
static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
static int n, m;
static final int MOD = 1000007;
static final int N = (int) (1e2 + 10);
static int[] a = new int[N];
static int[][] dp = new int[N][N];// 前i种花的前j盆有多少种可能
public static int nextInt() {
return cin.nextInt();
}
public static double nextDouble() {
return cin.nextDouble();
}
public static void main(String[] args) {
new Thread(null, new Main(), "", 1 << 20).start();
}
@Override
public void run() {
n = nextInt(); m = nextInt();
for (int i = 1; i <= n; i++) {
a[i] = nextInt();
}
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= a[i]; j++) {
for (int k = 0; k + j <= m; k++) {
if(j == 0 && k == 0) continue;
dp[i][k + j] = (dp[i - 1][k] + dp[i][k + j]) % MOD;
}
}
}
cout.println(dp[n][m] % MOD);
cout.flush();
}
}
浙公网安备 33010602011771号