【图论】C008_HDU 1317 XYZZY(spfa / floyd+ spfa / 一遍 spfa)

一、题目描述

最近发现了如何在Y-Crate游戏设备上运行开源软件。许多有进取心的设计师已经开发出部署在Y-Crate上的Advent风格的游戏。您的工作是测试许多这样的设计,看看哪些是可以取胜的。
每场比赛由多达100个房间组成。其中一个房间是起点,另一个房间是终点。每个房间的能量值都在-100到+100之间。单行门相互连接成对的房间。

玩家在起跑室开始时有 100 个能量点。她可以穿过连接她所在房间和另一个房间的任何门道,从而进入另一个房间。这个房间的能量值加到玩家的能量上。这一过程一直持续到她进入终点室获胜,或因精力耗尽而死亡(或沮丧地退出)。在她的冒险过程中,玩家可能会多次进入同一个房间,每次都会接收到它的能量。

能到达终点 n 输出 winnable,否则输出 hopeless


二、题解

方法一:SPFA 走正环

思路

题意:从一个开始有 100 的初始能量房间开始跑,每经过一个房间加上此房间的能量值,能量为负或正,问能不能到达终点。

注:在求最长路的时候只能用 spfa 或者是 Floyd 不能用基于贪心思想的 Dijkstra。

WA:一开始想法很简单,如果图存在负环, SPFA 不会让结点入队多次,也就很好地处理了负环问题。遇到正环就一直获取环里的能量,直到能量足够支撑到达终点为止。

未知错误:

  • Q1:为什么跑到足够多的能量 maxr 再出环,为什么不行呢?
    A1:题目是没有保证一定有正环呀,能

新技巧:SPFA 处理正环时,将三角不等式改为以下代码即可:

Arrays.fill(dist, 0);

if (dist[to] < dist[v] + w)
	dist[to] = dist[v] + w
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static int V, E, maxv = 100 + 50;
	static Edge[] edges;
	static int tot;
	static int[] head, cnt, dist;
	static boolean[] inq;
	static int INF = 0;
	static int maxr = 100 * 100 + 50;
	
	static void addEdge(int u, int v, int w) {
		edges[++tot] = new Edge();
		edges[tot].to = v;
		edges[tot].w = w;
		edges[tot].next = head[u];
		head[u] = tot;
	}
	
	static boolean spfa(int S) {
		Arrays.fill(dist, INF);
		dist[S] = 100;
		ArrayDeque<Integer> q = new ArrayDeque<>();
		q.add(S);
		inq[S] = true;
		
		while (!q.isEmpty()) {
			int v = q.poll();
			inq[v] = false;
			for (int i = head[v]; i!=0; i = edges[i].next) {
				int to = edges[i].to, w = edges[i].w;
				if (dist[to] < dist[v] + w && cnt[to] < maxr) {
					dist[to] = dist[v] + w;
					cnt[to]++;
					if (inq[to]) 
					    continue; 
					if (!q.isEmpty() && dist[to] < dist[q.peek()]) {
						q.addFirst(to);
					} else {
					    q.addLast(to);
					}
					inq[to] = true;
				}
			}
		}
		return dist[V] != INF;
	}
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
		while (true) {
			V = sc.nextInt();
			if (V == -1)
				break;
			dist = new int[maxv];
			head = new int[maxv];
			cnt = new int[maxv];
			inq = new boolean[maxv];
			edges = new Edge[maxv+50];
			for (int i = 1; i <= V; i++) {
				int cost = sc.nextInt();
				E = sc.nextInt();
				for (int j = 0; j < E; j++) {
				    int to = sc.nextInt();
					addEdge(i, to, cost);
				}
			}
			System.out.println(spfa(1) ? "winnable" : "hopeless");
		}
    }
    static class Edge {
        int to, w, next;
        public Edge() {}
    }
}

复杂度分析

  • 时间复杂度: O ( ) O()
  • 空间复杂度: O ( ) O()

方法二:Floyd + SPFA(未知错误)

  • 如果出现 cnt[v] >= V 则代表有正环,然后直接判断 v 是否与终点连通即可。
  • 否则,正常跑 spfa,最后返回 dist[V] > 0 即可。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
    static int V, E, maxv = 100 + 50;
    static Edge[] edges;
    static int tot;
    static int[] head, cnt, dist;
    static boolean[] inq;
    static int INF = -0x3f3f3f3f;
    static int maxr = 100 * 100 + 1000;
    static boolean[][] dp;
	
    static void addEdge(int u, int v, int w) {
        edges[++tot] = new Edge();
        edges[tot].to = v;
        edges[tot].w = w;
        edges[tot].next = head[u];
        head[u] = tot;
    }
    
	static void floyd() {
		for (int k = 1; k <= V; k++)
		for (int i = 1; i <= V; i++)
		for (int j = 1; j <= V; j++) {
			if (dp[i][j] || (dp[i][k] && dp[k][j]))
				dp[i][j] = true;
		}
	}
	
    static boolean spfa(int S) {
        Arrays.fill(dist, INF);
        dist[S] = 100;
        ArrayDeque<Integer> q = new ArrayDeque<>();
        q.add(S);
        inq[S] = true;
        
        while (!q.isEmpty()) {
            int v = q.poll();
            inq[v] = false;
			if (cnt[v] >= V)
				return dp[v][V];
				
            for (int i = head[v]; i!=0; i = edges[i].next) {
                int to = edges[i].to, w = edges[i].w;
                if (dist[to] < dist[v] + w && dist[v] + w > 0) {
                    dist[to] = dist[v] + w;
                    if (inq[to]) 
                        continue; 
                    if (!q.isEmpty() && dist[to] < dist[q.peek()]) {
                        q.addFirst(to);
                    } else {
                        q.addLast(to);
                    }
                    //  q.add(to);
                    inq[to] = true;
                     cnt[v]++;
                }
            }
        }
        return dist[V] != INF;
    }
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
        while (true) {
            V = sc.nextInt();
            if (V == -1)
                break;
            dist = new int[maxv];head = new int[maxv]; cnt = new int[maxv];inq = new boolean[maxv]; edges = new Edge[maxv+50];dp = new boolean[maxv][maxv];
            for (int i = 1; i <= V; i++) {
                int cost = sc.nextInt();
                E = sc.nextInt();
                for (int j = 0; j < E; j++) {
                    int to = sc.nextInt();
                    addEdge(i, to, cost);
                    dp[i][to] = true;
                }
            }
            floyd();
            System.out.println(spfa(1) ? "winnable" : "hopeless");
        }
    }
    static class Edge {
        int to, w, next;
        public Edge() {}
	}
}

复杂度分析

  • 时间复杂度: O ( V 3 ) O(V^3)
  • 空间复杂度: O ( . . . ) O(...)

方法三:一遍 spfa

  • 对于入队次数大于 V 次的结点,我们将其距离源点的距离设置为 INF,这样做,环中的结点的值就不会更新。
  • 这样做的后果有两个:
    • 环中的每一个结点都不和外面的点连通,最后 dist[V] 的值将会是 -INF
    • 环中的某些结点与外界(包括终点)是连通的,那么 dist[V] 的值将会被更新。

未知错误:没理由过不了的…

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
    static int V, E, maxv = 100 + 50;
    static Edge[] edges;
    static int tot;
    static int[] head, cnt, dist;
    static boolean[] inq;
    static int INF = 0x1fffffff;
	static int[] p;
    static void addEdge(int u, int v) {
        edges[tot] = new Edge();
        edges[tot].to = v;
        edges[tot].next = head[u];
        head[u] = tot++;
    }
    static boolean spfa(int S) {
        Arrays.fill(dist, -INF);
        dist[1] = 100;
        ArrayDeque<Integer> q = new ArrayDeque<>();
        q.add(1);
        inq[1] = true;
        cnt[1]++;
        while (!q.isEmpty()) {
            int v = q.poll();
            inq[v] = false;
			if (dist[v] + p[v] <= 0)
				continue;
            for (int i = head[v]; i!=0; i = edges[i].next) {
                int to = edges[i].to, w = p[v];
                if (dist[to] < dist[v] + w) {
                    dist[to] = dist[v] + w;
                    if (!inq[to])  {
						cnt[to]++;
						if (cnt[to] > V)
							continue;
						else if (cnt[to] == V)
							dist[to] = INF;
						q.add(to);
						inq[to] = true;
					}
                }
            }
        }
        return dist[V] > 0;
    }
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        while (true) {
            V = sc.nextInt();
            if (V == -1)
                break;
            dist = new int[maxv];head = new int[maxv]; cnt = new int[maxv];inq = new boolean[maxv]; edges = new Edge[maxv+50];
            p = new int[maxv];
			
            for (int i = 1; i <= V; i++) {
                int cost = sc.nextInt();
				p[i] = cost;
                E = sc.nextInt();
                for (int j = 0; j < E; j++) {
                    int to = sc.nextInt();
                    addEdge(i, to);
                }
               
            }
            System.out.println(spfa(1) ? "winnable" : "hopeless");
        }
    }
    static class Edge {
        int to, next;
        public Edge() {}
	}
}

复杂度分析

  • 时间复杂度: O ( k V ) O(kV)
  • 空间复杂度: O ( . . . ) O(...)

最长路习题:https://www.luogu.com.cn/problem/P1807
https://blog.csdn.net/WilliamSun0122/article/details/77948311

发布了691 篇原创文章 · 获赞 151 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/105519648
今日推荐