题意
多组数据
给出街道的数量
每个街道的点权
u v 权值等于街道的点权差相减的三次方(线段的终点减起点)
要求求出1到个个点的距离如果无法到达就输出?
因为边权是一个差的3次方,则有可能是负数。
所有有可能有负环, 负环上的点就不存在最短路径,所有找出负环,其他的点SPFA求最短路,如果连通就输出,不连通就归为无法到达的一类。如果在负环也是无法到达输出?。
用SPFA判断负环,如果一个点入队超过n次的话就dfs它,标记它所包含的连同分量。
此题不仅需要判断负环,还要求出不在负环上的点的最短路,应为在负环上的点是没有最短路的,那么我们只要标记出所有负环上的点,不对他们求最短路,这样spfa跑出来的就是不在环上点的最短路了
DFS+SPFA 利用系统栈,改变优化的顺序,如果有点优化了先前存在系统栈中的点,就判断有负环,并且此点在负环上
void SPFA(int s) {
if(cyclic[s]) return ;
vis[s] = true;
for(int i = h[s]; i; i = e[i].nes) {
if(cyclic[s]) return ;
int v = e[i].to;
int dis = e[i].w;
if(!cyclic[v]&&d[v] > d[s] + dis) {
d[v] = d[s] + dis;
if(!vis[v]) SPFA(v);
else dfs(v);
}
}
vis[s] = false;
}
BFS+SPFA 如果点入队次数超过n次 判断有负环,并且此点在环上
void SPFA(int s) {
queue<int> q;
memset(inq, 0, sizeof(inq));
memset(d, 0x3f, sizeof(d));
memset(nums, 0, sizeof(nums));
d[s] = 0;
q.push(s);
inq[s] = true;
nums[s]++;
while(!q.empty()) {
int u = q.front(); q.pop();
inq[u] = false;
if(cyclic[u]) continue; //此点在环上跳过
for(int i = h[u]; i; i = e[i].nes) {
int v = e[i].to;
int dis = e[i].w;
if(!cyclic[v] && d[v] > d[u] + dis) { //只优化不在环上的点
d[v] = d[u] + dis;
if(!inq[v]) {
q.push(v);
inq[v] = true;
if(++nums[v] > n) {
dfs(v); // 标记连同分量
}
}
}
}
}
}
dfs dfs标记函数
void dfs(int s) { //标记环
cyclic[s] = true;
for(int i = h[s]; i; i = e[i].nes) {
if(!cyclic[e[i].to]) dfs(e[i].to);
}
}
AC码
#include <cstring>
#include<cstdio>
#include<iostream>
#include<queue>
#include<unordered_map>
#define ll long long
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 10;
int d[maxn];
//bool vis[maxn];
int test1, n;
ll busy[maxn];
int h[maxn],p;
bool cyclic[maxn]; //用来记录点是否在环上
bool vis[maxn];
bool inq[maxn];
int nums[maxn];
struct edge {
ll nes, to, w;
}e[100000];
inline void add(int u, int v, int l) {
e[++p].nes = h[u];
e[p].to = v;
e[p].w = l;
h[u] = p;
}
void dfs(int s) {
cyclic[s] = true;
for(int i = h[s]; i; i = e[i].nes) {
if(!cyclic[e[i].to]) dfs(e[i].to);
}
}
//
//void SPFA(int s) {
// queue<int> q;
// memset(inq, 0, sizeof(inq));
// memset(d, 0x3f, sizeof(d));
// memset(nums, 0, sizeof(nums));
// d[s] = 0;
// q.push(s);
// inq[s] = true;
// nums[s]++;
// while(!q.empty()) {
// int u = q.front(); q.pop();
// inq[u] = false;
// if(cyclic[u]) continue;
// for(int i = h[u]; i; i = e[i].nes) {
// int v = e[i].to;
// int dis = e[i].w;
// if(!cyclic[v]
// && d[v] > d[u] + dis) {
// d[v] = d[u] + dis;
// if(!inq[v]) {
// q.push(v);
// inq[v] = true;
// if(++nums[v] > n) {
// dfs(v);
// }
// }
// }
// }
// }
//}
void SPFA(int s) {
if(cyclic[s]) return ;
vis[s] = true;
for(int i = h[s]; i; i = e[i].nes) {
if(cyclic[s]) return ;
int v = e[i].to;
int dis = e[i].w;
if(!cyclic[v]&&d[v] > d[s] + dis) {
d[v] = d[s] + dis;
if(!vis[v]) SPFA(v);
else dfs(v);
}
}
vis[s] = false;
}
inline ll bolck(ll u, ll v) {
ll ans = 1;
for(int i = 0; i < 3; i++) {
ans *= (busy[v] - busy[u]);
}
return ans;
}
int main() {
scanf("%d", &test1);
int _case = 0;
while(test1--) {
memset(cyclic, false,sizeof(cyclic));
memset(h, 0, sizeof(h));
p = 0;
//memset(e, 0, sizeof(e));
int m, query;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%lld", &busy[i]);
}
scanf("%d", &m);
for(int i = 1; i <= m; i++) {
ll u, v, l;
scanf("%d%d", &u, &v);
l = bolck(u, v);
add(u, v, l);
}
scanf("%d", &query);
memset(vis, 0, sizeof(vis));
memset(d, 0x3f, sizeof(d));
d[1] = 0;
SPFA(1);
printf("Case %d:\n", ++_case);
while(query--) {
int w;
scanf("%d", &w);
if(cyclic[w] || d[w] == inf || d[w] < 3) printf("?\n");
else printf("%d\n", d[w]);
}
}
return 0;
}
dfs 判断负环
inline bool dfs_spfa(int u)//dfs版的spfa(其实与bfs的差不多,只是改了搜索顺序)
{
vis[u]=1;
for(int i=head[u],v;i;i=edge[i].nxt)
{
v=edge[i].to;
if(dis[v]>dis[u]+edge[i].w)
{
dis[v]=dis[u]+edge[i].w;
if(vis[v] || dfs_spfa(v)) return 1;//如果访问过了(即找到负环)或者已经存在了负环
}
}
vis[u]=0;//注意改回来
return 0;//没找到
}