Week7 作业——C - TT 的美梦

题目

这一晚,TT 做了个美梦!

在梦中,TT 的愿望成真了,他成为了喵星的统领!喵星上有 N 个商业城市,编号 1 ~ N,其中 1 号城市是 TT 所在的城市,即首都。

喵星上共有 M 条有向道路供商业城市相互往来。但是随着喵星商业的日渐繁荣,有些道路变得非常拥挤。正在 TT 为之苦恼之时,他的魔法小猫咪提出了一个解决方案!TT 欣然接受并针对该方案颁布了一项新的政策。

具体政策如下:对每一个商业城市标记一个正整数,表示其繁荣程度,当每一只喵沿道路从一个商业城市走到另一个商业城市时,TT 都会收取它们(目的地繁荣程度 - 出发地繁荣程度)^ 3 的税。

TT 打算测试一下这项政策是否合理,因此他想知道从首都出发,走到其他城市至少要交多少的税,如果总金额小于 3 或者无法到达请悄咪咪地打出 ‘?’。

Input

第一行输入 T,表明共有 T 组数据。(1 <= T <= 50)

对于每一组数据,第一行输入 N,表示点的个数。(1 <= N <= 200)

第二行输入 N 个整数,表示 1 ~ N 点的权值 a[i]。(0 <= a[i] <= 20)

第三行输入 M,表示有向道路的条数。(0 <= M <= 100000)

接下来 M 行,每行有两个整数 A B,表示存在一条 A 到 B 的有向道路。

接下来给出一个整数 Q,表示询问个数。(0 <= Q <= 100000)

每一次询问给出一个 P,表示求 1 号点到 P 号点的最少税费。

Output

每个询问输出一行,如果不可达或税费小于 3 则输出 ‘?’。

Sample Input

2
5
6 7 8 9 10
6
1 2
2 3
3 4
1 5
5 4
4 5
2
4
5
10
1 2 4 4 5 6 7 8 9 10
10
1 2
2 3
3 1
1 4
4 5
5 6
6 7
7 8
8 9
9 10
2
3 10

Sample Output

Case 1:
3
4
Case 2:
?
?

负权环路

在Bellman-ford算法中如何判断负环的存在? • 如果存在负环,那么最短路经过的边数会大于等于n
• 一些边被松弛的次数会大于等于n
• 如果在第n 次松弛操作时还存在边能够被成功松弛,那么图中存在 负环
在这里插入图片描述
SPFA 算法是Bellman-ford算法的队列优化,负环存在的判断条件与 Bellman-ford算法一致• 思考:如何判断一条边被松弛的次数大于等于n?

• SPFA 中将点加入队列,不容易判断边的松弛次数
• 可以放宽条件,判断点的入队次数,如果某一点入队n 次则说明有 负环
• 可以判断最短路的边数,如果到某一点的最短路的边数超过了n-1 则说明有负环(本题使用)
修改后的SPFA 算法
• cnt[x] 表示x当前最短 路上的边数
• 第16行和第17行记 录了v的最短路长度 cnt[v],然后判断cnt[v] 大于等于n的话就说明 找到了负环。
在这里插入图片描述

思路

题目要求从1 出发到其他点花费最少的钱,相当于求1 到其他点的最短路。
• 值得注意的一点是,经过一条道路,TT 收的税可能是负数。
• 图中存在负权边,这是一个含负权边的单源最短路径问题
• 当什么情况下会输出‘?’ • 不可达 • 最短路小于3 • 最短路存在 • 最短路不存在
• 需要我们判断负环,那些通过负环可以到达的点的最短路均不存在
• 找到负环后,将负环所在连通块里的点打上标记即可

1、用链式前向星保存图
2、调用spfa算法,先将1放入队列中,并将inq[1]=1,dis[1]=0
3、不断从队列头部取出之前入队的点,将inq[x]置为0,遍历由这点发出的有向边,判断可否用这些有向边来更新另一端点的距离(考虑到长度为3的最短路径只可由长度为2的最短路径加上一条边长组成,否则若可用长度为1的最短路径加上一条边组成,则在更新长度为2的最短路径时必然会将该路径更新,所以当想要得到长度为i的最短路径时,只需考虑在更新长度i-1的最短路径中更新过的节点即可,所以在每次更新一个长度的最短路径的节点时将新更新的节点放入队列中,在更新下一长度时只需从这些节点往外延伸一条边来更新即可),当可以更新时,同时更新记录到该点最短路径长度的数组cnt,判断cnt[y]是否>=n,若>=n,则调用dfs(y),将所有y可到达的点的fu标为1。
4、判断3中取出的新端点是否已在栈中且不是负环可到达的顶点,在满足条件时,将该点加入队列。

错误

曾出现错误作为注释标注在代码中

代码

#include<iostream>
#include<queue>
#include<cmath>
#include<string.h>
using namespace std;
const int maxn=201;
const int maxm=100010;
const int INF=0x3f3f3f3f;
struct edge
{
 int to,next,w; 
}e[maxm];
int n,a[maxn],head[maxn],tot,dis[maxn],cnt[maxn];
bool inq[maxn],fu[maxn];
queue<int> que;
void add(int x,int y,int w)
{
 e[++tot].to=y;
 e[tot].next=head[x];
 e[tot].w=w;
 head[x]=tot;
}
void dfs(int x)
{
 fu[x]=1; 
 for(int i=head[x];i;i=e[i].next)
 {
  int y=e[i].to;
  if(!fu[y])
  {
   fu[y]=1;
   dfs(y);
  }
 }
}
void spfa(int s)
{
 while(que.size())
  que.pop();
 for(int i=1;i<=n;i++)
 {
  inq[i]=0;
  cnt[i]=0;
  dis[i]=INF;
  fu[i]=0;
 }
 dis[s]=0;
 inq[s]=1;
 que.push(s);
 while(!que.empty())
 {
  int x=que.front();
  que.pop();
  inq[x]=0;
  for(int i=head[x];i;i=e[i].next)
  {
   int y=e[i].to;
   if(dis[y]>dis[x]+e[i].w)
   {
    dis[y]=dis[x]+e[i].w;
    cnt[y]=cnt[x]+1;
    if(cnt[y]>=n)
    {
     dfs(y);
    }
    if(!inq[y]&&!fu[y])
    {
     inq[y]=1;
     que.push(y);
    }
   }
  }
 }
}
int main()
{
 int t,m,A,B,q,p;
 scanf("%d",&t);
 for(int i=0;i<t;i++)
 {
  tot=1;
  memset(head,0,sizeof(head));//将head清空要在录入边之前。 
  scanf("%d",&n);
  for(int j=1;j<=n;j++)
   scanf("%d",&a[j]);
  scanf("%d",&m);
  for(int j=0;j<m;j++)
  {
   scanf("%d%d",&A,&B);
   int w=pow((a[B]-a[A]),3);
   add(A,B,w);
  }
  spfa(1);
  scanf("%d",&q);
  printf("Case %d:\n",i+1);//注意输出格式 
  for(int j=0;j<q;j++)
  {
   scanf("%d",&p);
   if(dis[p]==INF||dis[p]<3||fu[p]==1)
    printf("?\n");
   else
   {
    printf("%d\n",dis[p]);
   }
  }
 }
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值