【Week7作业 C】TT的美梦【SPFA】

题意:

这一晚,TT 做了个美梦!
在梦中,TT 的愿望成真了,他成为了喵星的统领!喵星上有 N 个商业城市,编号 1 ~ N,其中 1 号城市是 TT 所在的城市,即首都。
喵星上共有 M 条有向道路供商业城市相互往来。但是随着喵星商业的日渐繁荣,有些道路变得非常拥挤。正在 TT 为之苦恼之时,他的魔法小猫咪提出了一个解决方案!TT 欣然接受并针对该方案颁布了一项新的政策。
具体政策如下:对每一个商业城市标记一个正整数,表示其繁荣程度,当每一只喵沿道路从一个商业城市走到另一个商业城市时,TT 都会收取它们(目的地繁荣程度 - 出发地繁荣程度)^ 3 的税。
TT 打算测试一下这项政策是否合理,因此他想知道从首都出发,走到其他城市至少要交多少的税,如果总金额小于 3 或者无法到达请悄咪咪地打出 ‘?’。
注意:多组数据,第i组数据第一行要输出Case i:。


思路:

求1到其他点的最短路,图中存在负权边,可能存在负环。所以只能用SPFA来求单源最短路径。
在SPFA中要判断负环是否存在:如果存在负环,那么最短路经过的边数会>=n,一些边被松弛的次数会>=n。也就是说到某一点的最短路的边数超过了n-1则说明有负环。用cnt[v]来记录到达v的最短路边数,若cnt[v]>=n则找到负环。
找到负环后需要将负环所在的连通块进行标记,在松弛时要跳过这些连通块里的顶点。使用dfs来进行标记,若顶点在负环所在连通块里,则fuhuan[v]=1。


总结:

一道SPFA的题目,需要对负环进行处理。输出有多组数据,且第i组数据要输出一行Case i:,就很容易忽略而WA。


代码:

#include <iostream>
#include <queue> 
using namespace std;

int INF=1e8;
int n,m,q,p;
int a[201];
//链式前向星
struct edge
{
	int to,next,w;
};
edge e[100010];
int head[201],tot;
void add(int x,int y,int w)
{
	e[++tot].to=y,e[tot].next=head[x];
	e[tot].w=w,head[x]=tot;
}
int dis[201],inq[201],cnt[201];
//inq为1在队列中;cnt用于判断是否存在负环 
bool fuhuan[201];	//在负环所在连通块为1 
queue <int> qu;
void dfs(int v)	//用于标记负环所在连通块 
{
	fuhuan[v]=1;
	for(int i=head[v];i!=0;i=e[i].next)
	{
		int next=e[i].to;
		if(fuhuan[next]==0)
			dfs(next);
	}
 } 
void SPFA(int s)
{
	for(int i=1;i<=n;i++)
		dis[i]=INF,inq[i]=0,cnt[i]=0;
	dis[s]=0,inq[s]=1;
	qu.push(s);
	while(!qu.empty())
	{
		int u=qu.front();
		qu.pop();
		inq[u]=0;
		for(int i=head[u]; i!=0; i=e[i].next)
		{
			int v=e[i].to,w=e[i].w;
			if(fuhuan[v])	continue;
			if(dis[v]>dis[u]+w)
			{
				cnt[v]=cnt[u]+1;
				if(cnt[v]>=n)	//找到负环 
				{
					dfs(v);
				}
				dis[v]=dis[u]+w;
				if(!inq[v])
				{
					qu.push(v);
					inq[v]=1;
				}
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	int T;
	cin>>T;
	for(int sj=1;sj<=T;sj++)
	{
		//初始化
		for(int i=1;i<=200;i++)
			head[i]=0,fuhuan[i]=0;
		tot=0; 
		//读入 
		cin>>n;
		for(int i=1;i<=n;i++)
			cin>>a[i];
		cin>>m;
		for(int i=0;i<m;i++)
		{
			int A,B;
			cin>>A>>B;
			int w=(a[B]-a[A])*(a[B]-a[A])*(a[B]-a[A]);
			add(A,B,w);
		}
		//求距离 
		SPFA(1);
		cin>>q;
		cout<<"Case "<<sj<<":"<<endl;
		for(int op=0;op<q;op++)
		{
			cin>>p;
			//输出税费 
			if(dis[p]==INF||dis[p]<3||fuhuan[p]==1)
				cout<<"?"<<endl;
			else	cout<<dis[p]<<endl;
		}
		
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SPFA算法是一种基于Bellman-Ford算法的单源最短路径算法,其主要思想是通过队列来遍历节点,不断更新节点的距离值,直到所有节点的距离值都不再发生变化为止。以下是SPFA算法的C语言实现代码: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAX_N 1000 #define INF 0x7fffffff typedef struct edge { int to; int w; struct edge *next; } Edge; Edge *G[MAX_N + 1]; int dist[MAX_N + 1]; bool in_queue[MAX_N + 1]; void add_edge(int u, int v, int w) { Edge *e = (Edge *)malloc(sizeof(Edge)); e->to = v; e->w = w; e->next = G[u]; G[u] = e; } void spfa(int s, int n) { for (int i = 1; i <= n; i++) { dist[i] = INF; in_queue[i] = false; } dist[s] = 0; in_queue[s] = true; int q[MAX_N], head = 0, tail = 1; q[head] = s; while (head != tail) { int u = q[head++]; if (head >= MAX_N) { head = 0; } in_queue[u] = false; for (Edge *e = G[u]; e != NULL; e = e->next) { int v = e->to; if (dist[v] > dist[u] + e->w) { dist[v] = dist[u] + e->w; if (!in_queue[v]) { q[tail++] = v; if (tail >= MAX_N) { tail = 0; } in_queue[v] = true; } } } } } int main() { int n, m, s; scanf("%d%d%d", &n, &m, &s); for (int i = 0; i < m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); add_edge(u, v, w); } spfa(s, n); for (int i = 1; i <= n; i++) { printf("%d ", dist[i]); } printf("\n"); return 0; } ``` 其中,`G`是邻接表,存储图的信息;`dist`是从源点到各个节点的最短距离数组;`in_queue`记录每个节点是否在队列中。`add_edge`函数用于添加边,`spfa`函数实现了SPFA算法的主体部分,`main`函数用于读入图的信息,调用`spfa`函数求解最短路径,并输出结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值