1137. 选择最佳线路 - AcWing题库
原问题:从每个起点出发,到达终点的所有路线长度的最小值
加了虚拟远点之后:从虚拟远点出发,到达终点的所有路线的最小值 173. 矩阵距离 - AcWing题库(虚拟远点,思路一样)
法1:虚拟远点 (这里的idx一定要重置!!)
//虚拟远点
import java.util.*;
import java.io.*;
public class Main{
static int N = 1010, M = 21010, INF = 0x3f3f3f3f;//这里M开多1000是因为可能建立了1000条虚拟远点边
static int[] dist = new int[N];
static int[] h = new int[N], e = new int[M], ne = new int[M], w = new int[M];
static int n, m, s, idx;
static boolean[] st = new boolean[N];//是否在循环队列中
//邻接表存储
public static void add(int a, int b, int c){
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx ++;
}
public static int spfa(){
Arrays.fill(dist, INF);
dist[0] = 0;
Queue<Integer> q = new LinkedList<>();
q.offer(0);
while(!q.isEmpty()){
int t = q.poll();
st[t] = false;
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
if(dist[j] > dist[t] + w[i]){
dist[j] = dist[t] + w[i];
if(!st[j]){
q.offer(j);
st[j] = true;
}
}
}
}
if(dist[s] == INF) return -1;
else return dist[s];
}
public static void main(String[] args)throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while(true){
String temp = br.readLine();
if(temp == null) break;//测试数据结束
String[] arr = temp.split(" ");
n = Integer.parseInt(arr[0]);
m = Integer.parseInt(arr[1]);
s = Integer.parseInt(arr[2]);
Arrays.fill(h, -1);//初始化队头
idx = 0;//这里的idx一定要记得重置
//建图
for(int i = 1; i <= m; i ++){
String[] str = br.readLine().split(" ");
int p = Integer.parseInt(str[0]);
int q = Integer.parseInt(str[1]);
int t = Integer.parseInt(str[2]);
add(p, q, t);
}
int w = Integer.parseInt(br.readLine());//起点个数
String[] s1 = br.readLine().split(" ");
for(int i = 0; i < w; i ++){
int x = Integer.parseInt(s1[i]);
add(0, x, 0);//建立虚拟远点和起点的联系
}
System.out.println(spfa());
}
}
}
法2:反向建图
import java.util.*;
import java.io.*;
public class Main{
static int N = 1010, M = 20010, INF = 0x3f3f3f3f;
static int n, m, S, idx, W;
static int[] h = new int[N], ne = new int[M], e = new int[M], w = new int[M];
static int[] dist = new int[N];
static boolean[] st = new boolean[N];
static int[] start = new int[N];//起点
public static void add(int a, int b, int c){
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx ++;
}
public static void spfa(){
Arrays.fill(dist, INF);
dist[S] = 0;
Queue<Integer> q = new LinkedList<>();
q.offer(S);
while(!q.isEmpty()){
int t = q.poll();
st[t] = false;
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
if(dist[j] > dist[t] + w[i]){
dist[j] = dist[t] + w[i];
if(!st[j]){
q.offer(j);
st[j] = true;
}
}
}
}
}
public static void main(String[] args)throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while(true){
String temp = br.readLine();
if(temp == null) break;
String[] s1 = temp.split(" ");
n = Integer.parseInt(s1[0]);
m = Integer.parseInt(s1[1]);
S = Integer.parseInt(s1[2]);
Arrays.fill(h, -1);//每次都要重置
idx = 0;
for(int i = 1; i <= m; i ++){
String[] s2 = br.readLine().split(" ");
int p = Integer.parseInt(s2[0]);
int q = Integer.parseInt(s2[1]);
int t = Integer.parseInt(s2[2]);
add(q, p, t);
}
W = Integer.parseInt(br.readLine());
String[] s3 = br.readLine().split(" ");
for(int i = 0; i < W; i ++){
start[i] = Integer.parseInt(s3[i]);
}
spfa();
int res = INF;
for(int i = 0; i < W; i ++){
res = Math.min(res, dist[start[i]]);
}
if(res == INF) System.out.println("-1");
else System.out.println(res);
}
}
}
1131. 拯救大兵瑞恩 - AcWing题库
import java.util.*;
class PII{
int x, y;
public PII(int x, int y){
this.x = x;//这个格子的编号
this.y = y;//所持有钥匙的状态
}
}
public class Main{
//M为4*n*(n-1)
static int N = 11, M = 360, P = 1 << 10, INF = 0x3f3f3f3f;
static int n, m, p, k, s, idx;
static int[] h = new int[N * N], ne = new int[M], e = new int[M], w = new int[M];
static int[][] g = new int[N * N][N * N];//g[x][y]表示x行y列的格子的编号
static int[] key = new int[P];//格子的初始钥匙状态
static int[][] dist = new int[N * N][P];//每个格子对应的编号所持有钥匙的状态
static boolean[][] st = new boolean[N * N][P];//判断是否用过
static boolean[][] line = new boolean[N * N][N * N];//用于建图,格子与格子之间连接的状态
//邻接表存储
public static void add(int a, int b, int c){
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx ++;
}
//剩余所有边连接
public static void build(){
int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
for(int u = 0; u < 4; u ++){
int x = i + dx[u];
int y = j + dy[u];
if(x < 1 || x > n || y < 1 || y > m) continue;
int a = g[i][j], b = g[x][y];
if(!line[a][b]) add(a, b, 0);
}
}
}
}
public static int bfs(){
for(int i = 0; i < N * N; i ++){
Arrays.fill(dist[i], INF);
}
dist[1][0] = 0;//起点的距离是0,坐标(1,1)的编号是1,什么钥匙都没有,状态为0
//双端队列
Deque<PII> q = new LinkedList<>();
q.offer(new PII(1, 0));
while(!q.isEmpty()){
PII t = q.poll();
if(st[t.x][t.y]) continue;
st[t.x][t.y] = true;
if(t.x == n * m) return dist[t.x][t.y];//走到了终点
//1.如果这个位置有钥匙,就要捡起来
if(key[t.x] != 0){
int state = t.y | key[t.x];
if(dist[t.x][state] > dist[t.x][t.y]){
dist[t.x][state] = dist[t.x][t.y];
q.offerFirst(new PII(t.x, state));//0加入队头
}
}
//2.更新这个点的所有出边
for(int i = h[t.x]; i != -1; i = ne[i]){
int j = e[i];
//有门,没有钥匙
if(w[i] != 0 && ((t.y >> w[i] - 1 & 1) == 0)) continue;
if(dist[j][t.y] > dist[t.x][t.y] + 1){
dist[j][t.y] = dist[t.x][t.y] + 1;
q.offerLast(new PII(j, t.y));//1加入队尾
}
}
}
return -1;
}
//开始main函数
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
p = sc.nextInt();
k = sc.nextInt();
Arrays.fill(h, -1);
for(int i = 1, t = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
g[i][j] = t ++;//给格子编号
}
}
while(k -- > 0){
int x1 = sc.nextInt();
int y1 = sc.nextInt();
int x2 = sc.nextInt();
int y2 = sc.nextInt();
int c = sc.nextInt();
int a = g[x1][y1];
int b = g[x2][y2];
line[a][b] = true;
line[b][a] = true;//标记为已经连接过边了,无论是墙还是门
if(c != 0){
add(a, b, c);//连接双向边,边权是门的型号
add(b, a, c);
}
}
build();//建立其他通路,不是门也不是墙
s = sc.nextInt();//钥匙的数量
while(s -- > 0){
int x = sc.nextInt();
int y = sc.nextInt();
int c = sc.nextInt();
//由于格子的编号是从1开始的
key[g[x][y]] |= 1 << c - 1;
}
//最后输出bfs
System.out.print(bfs());
}
}
1134. 最短路计数 - AcWing题库
import java.util.*;
import java.io.*;
public class Main{
static int N = 100010, M = 4 * N, INF = 0x3f3f3f3f;//M因为无向边,有可能有重边和自环
static int n, m, idx;
static int[] h = new int[N], e = new int[M], ne = new int[M];
static int[] dist = new int[N];
static int[] cnt = new int[N];
static int[] q = new int[N];//bfs队列
static boolean[] st = new boolean[N];
public static void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
public static void bfs(){
Arrays.fill(dist, INF);
int hh = 0, tt = -1;
q[++ tt] = 1;
dist[1] = 0;
cnt[1] = 1;
while(hh <= tt){
int t = q[hh ++];
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
if(dist[j] > dist[t] + 1){
dist[j] = dist[t] + 1;
cnt[j] = cnt[t];
if(!st[j]){
q[++ tt] = j;
st[j] = true;
}
}else if(dist[j] == dist[t] + 1){
cnt[j] = (cnt[j] + cnt[t]) % 100003;
}
}
}
}
public static void main(String[] args)throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] arr = br.readLine().split(" ");
n = Integer.parseInt(arr[0]);
m = Integer.parseInt(arr[1]);
Arrays.fill(h, -1);
while(m -- > 0){
String[] str = br.readLine().split(" ");
int a = Integer.parseInt(str[0]);
int b = Integer.parseInt(str[1]);
add(a, b);
add(b, a);//无向图
}
bfs();
for(int i = 1; i <= n; i ++){
System.out.println(cnt[i]);
}
}
}
383. 观光 - AcWing题库
先算出最短路径和最短路径的条数,次短路径和次短路径的条数,再判断最短路径是不是比次短路径多1,如果是的话,就把次短路径的条数加上,否则就不加。
import java.util.*;
import java.io.*;
class PII implements Comparable<PII>{
int ver, type, dist;
public PII(int ver, int type, int dist){
this.ver = ver;
this.type = type;
this.dist = dist;
}
public int compareTo(PII o){
return dist - o.dist;
}
}
public class Main{
static int N = 1010, M = 10010, INF = 0x3f3f3f3f;
static int n, m, S, F, idx;
static int[] h = new int[N], e = new int[M], ne = new int[M], w = new int[M];
static int[][] dist = new int[N][2];//dist[i][0]表示最短 dist[i][1]表示次短
static int[][] cnt = new int[N][2];
static boolean[][] st = new boolean[N][2];
public static void add(int a, int b, int c){
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx ++;
}
public static int dijkstra(){
for(int i = 0; i < N; i ++){
Arrays.fill(dist[i], INF);
Arrays.fill(st[i], false);
Arrays.fill(cnt[i], 0);
}
PriorityQueue<PII> q = new PriorityQueue<>();//优先队列
q.offer(new PII(S, 0, 0));//起点,类型为最短,还没有距离
dist[S][0] = 0;//起点只有最短距离,没有次短距离
cnt[S][0] = 1;//1条最短距离
while(!q.isEmpty()){
PII t = q.poll();
int ver = t.ver;
int type = t.type;
int distance = t.dist;
int count = cnt[ver][type];
if(st[ver][type]) continue;
st[ver][type] = true;
for(int i = h[ver]; i != -1; i = ne[i]){
int j = e[i];
if(dist[j][0] > distance + w[i]){
dist[j][1] = dist[j][0];
dist[j][0] = distance + w[i];
cnt[j][1] = cnt[j][0];
cnt[j][0] = count;
q.offer(new PII(j, 1, dist[j][1]));
q.offer(new PII(j, 0, dist[j][0]));
}else if(dist[j][0] == distance + w[i]){
cnt[j][0] += count;
}else if(dist[j][1] > distance + w[i]){
dist[j][1] = distance + w[i];
cnt[j][1] = count;
q.offer(new PII(j, 1, dist[j][1]));
}else if(dist[j][1] == distance + w[i]){
cnt[j][1] += count;
}
}
}
int res = cnt[F][0];
if(dist[F][0] + 1 == dist[F][1]) res += cnt[F][1];
return res;
}
public static void main(String[] args)throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine());
while(T -- > 0){
String[] s1 = br.readLine().split(" ");
n = Integer.parseInt(s1[0]);
m = Integer.parseInt(s1[1]);
Arrays.fill(h, -1);
idx = 0;
while(m -- > 0){
String[] s2 = br.readLine().split(" ");
int a = Integer.parseInt(s2[0]);
int b = Integer.parseInt(s2[1]);
int c = Integer.parseInt(s2[2]);
add(a, b, c);//有向边
}
String[] s3 = br.readLine().split(" ");
S = Integer.parseInt(s3[0]);
F = Integer.parseInt(s3[1]);
System.out.println(dijkstra());
}
}
}