程序设计思维Week7-作业
C-TT的美梦
Description
有 N 个商业城市,编号 1 ~ N,其中 1 号城市是 TT 所在的城市,即首都。
喵星上共有 M 条有向道路在商业城市相互往来。
对每一个商业城市标记一个正整数,表示其繁荣程度,当每一只喵沿道路从一个商业城市走到另一个商业城市时,TT 都会收取它们(目的地繁荣程度 - 出发地繁荣程度)^ 3 的税。
从首都出发,走到其他城市至少要交多少的税,如果总金额小于 3 或者无法到达请悄咪咪地打出 '?'。
第一行输入 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 号点的最少税费。
每个询问输出一行,如果不可达或税费小于 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
Output:
Case 1:
3
4
Case 2:
?
?
Idea
根据题意要求从1号出发到其他城市花费最少的钱即求1到其他点的单源最短路,但是这里的边权是(a[x]-a[y])^3,存在负权边,所以我们明确用SPFA求带负权边的单源最短路。
首先用前向星存储边,然后把起点压入队列进行SPFA,每次从队列取出一个点,遍历它指向其他点的边,如果找到更短的路径,就更新距离dis并把对应的点压入队列,每个点不会在队列同时出现多次,同时记录当前路径松弛的次数cnt。但因为存在负权边就有可能存在负环,路径会不停地绕着负环行走即不断地松弛,当松弛的次数cnt超过节点数意味着出现了负环,我们需要找到负环并用DFS从当前在负环的点出发把所在连通块里的点打上标记认作到这些点的最短路不存在。完成SPFA后,根据查询输出1号到其的最短路长度,当距离无穷大或最短路小于3或路径中有负环时输出?。
Summary
这题考察带负权边的单源最短路问题,基本思想是SPFA+DFS标记负环,如果路径中有负环则最短路是不存在的。
所谓连通块意思是,目前的点位于负环中,从该点出发沿着边向前扩展直至没有边可用,那么遍历到的这些点都应该被打上标记。
第一个continue表示如果取出的点已经被标记路径中有负环则不需要再进行接下来的操作。但第二个continue不能存在(注释中),可能会导致遍历边的循环无法停止,这也是我T了好几次的原因。
注意初始化各个数组,另外不要忘记输出中的“Case 1:”…
Codes
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int inf = 100000;
int T,N,M,Q,p;
int a[220],dis[220],pre[220],inq[220],cnt[220],head[220],vis[220];
queue<int> q;
struct edge {
int v,w,next=-1;
}e[100100];
void init() {
for (int i = 1; i <= N; i++) {
dis[i] = inf;
pre[i] = 0;
inq[i] = 0;
cnt[i] = 0;
vis[i] = 0;
head[i] = -1;
}
}
void dfs(int i) {
vis[i] = 1;
for (edge t = e[head[i]];head[i]!=-1; t = e[t.next]) {
if(vis[t.v]==0)dfs(t.v);
if (t.next == -1)break;
}
}
int main()
{
cin.sync_with_stdio(false);
cin >> T;
int num = 1;
while (T--) {
cin >> N;
init();
for (int i = 1; i <= N; i++)
cin >> a[i];
cin >> M;
for (int i = 1; i <= M; i++) {
int u, v;
cin >> u>> v;
e[i].v = v;
e[i].w =(a[v] - a[u])*(a[v] - a[u])*(a[v] - a[u]);
e[i].next = head[u];
head[u] = i;
}
dis[1] = 0;
inq[1] = 1;
q.push(1);
while (!q.empty())
{
int u = q.front(); q.pop();
inq[u] = 0;
if (vis[u])continue;
for (edge t = e[head[u]];; t = e[t.next]) {
int v = t.v;
//if (vis[v])continue;
if (dis[v] > dis[u] + t.w) {
cnt[v] = cnt[u] + 1;
if (cnt[v] >= N) {
//找到负环
dfs(v);
}
dis[v] = dis[u] + t.w;
//pre[v] = u;
if (!inq[v]) {
q.push(v);
inq[v] = 1;
}
}
if (t.next == -1)break;
}
}
cin >> Q;
printf("Case %d:\n", num);
while (Q--) {
cin >> p;
if (dis[p] == inf || dis[p] < 3|| (vis[p]==1))printf("?\n");
else printf("%d\n", dis[p]);
}
num++;
}
}