A - TT 的魔法猫
众所周知,TT 有一只魔法猫。
这一天,TT 正在专心致志地玩《猫和老鼠》游戏,然而比赛还没开始,聪明的魔法猫便告诉了 TT 比赛的最终结果。TT 非常诧异,不仅诧异于他的小猫咪居然会说话,更诧异于这可爱的小不点为何有如此魔力?
魔法猫告诉 TT,它其实拥有一张游戏胜负表,上面有 N 个人以及 M 个胜负关系,每个胜负关系为 A B,表示 A 能胜过 B,且胜负关系具有传递性。即 A 胜过 B,B 胜过 C,则 A 也能胜过 C。
TT 不相信他的小猫咪什么比赛都能预测,因此他想知道有多少对选手的胜负无法预先得知,你能帮帮他吗?
Input
第一行给出数据组数。
每组数据第一行给出 N 和 M(N , M <= 500)。
接下来 M 行,每行给出 A B,表示 A 可以胜过 B。
Output
对于每一组数据,判断有多少场比赛的胜负不能预先得知。注意 (a, b) 与 (b, a) 等价,即每一个二元组只被计算一次。
Example Input
3
3 3
1 2
1 3
2 3
3 2
1 2
2 3
4 2
1 2
3 4
Example Output
0
0
4
题意:
有一张游戏胜负表,上面若干 个胜负关系,且胜负关系具有传递性,计算出有多少对选手的胜负无法预知。
分析:
本题应该使用Floyd算法,使用一个二维数dis组存储两者间的胜负关系。
从x到y的最短路为min(dis[x][y],dis[x][k]+dis[k][y]),即目前x、y间最短距离 和 x、y通过中间点k连通的距离的较小一方。这里可以进行剪枝,即当dis[x][k]为0时,不论dis[k][y]是否为1,都不能改变dis[x][y]。
代码如下:
#include<iostream>
using namespace std;
int dis[510][510] = { 0 };
int main()
{
int n, m;
int num;
int ans;
cin >> num;
while (num--)
{
ans = 0;
cin >> n >> m;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
dis[i][j] = 0;
}
};
for (int j = 0; j < m; j++)
{
int a, b;
cin >> a >> b;
dis[a - 1][b - 1] = 1;
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (dis[j][i] == 1)
{
for (int k = 0; k < n; k++)
dis[j][k] = dis[j][k] | (dis[j][i] & dis[i][k]);
}
}
}
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (dis[i][j] == 0 && dis[j][i] == 0)
ans++;
}
}
cout << ans << endl;
}
return 0;
}
B - TT 的旅行日记
众所周知,TT 有一只魔法猫。
今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!
Input
输入包含多组数据。每组数据第一行为 3 个整数 N, S 和 E (2 ≤ N ≤ 500, 1 ≤ S, E ≤ 100),即猫猫快线中的车站总数,起点和终点(即喵星机场所在站)编号。
下一行包含一个整数 M (1 ≤ M ≤ 1000),即经济线的路段条数。
接下来有 M 行,每行 3 个整数 X, Y, Z (1 ≤ X, Y ≤ N, 1 ≤ Z ≤ 100),表示 TT 可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。
下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
接下来 K 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。
Output
对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点),第二行是 TT 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行是 TT 前往喵星机场花费的总时间。
本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行
Example Input
4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3
Example Output
1 2 4
2
5
题意:
在图中找到一条花费时间最短的路线并输出。
分析:
本题类似于求单源最短路径,但要对商业线进行特殊处理并输出经过的点以及中转的点。
算法思想为从起点和终点分别进行两次dijkstra,分别记录单源最短路,然后对商业线进行枚举并记下使用商业线的最小值,与不使用商业线的最小值进行比较,最后输出最小值。
代码如下:
#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
#include <cmath>
#include <string.h>
using namespace std;
const int inf = 100000000;
struct zhan
{
int xx, tp;
};
struct edge
{
int a;
int to, w;
bool operator<(const edge& ee)const
{
return w > ee.w;
}
};
int n, S, E;
int m, k, cnt = 0, off = -1;
int dis[510][2];
int road[510];
zhan pre[510][2];
vector<edge> e[510];
void find(int end, int p)
{
if (end == S)
{
road[cnt] = S;
cnt++;
return;
}
zhan before = pre[end][p];
if (before.tp == 1)
{
off = before.xx;
p = 0;
}
find(before.xx, p);
road[cnt] = end;
cnt++;
return;
}
int main()
{
int kk = 0;
while (scanf("%d", &n) != EOF)
{
scanf("%d %d", &S, &E);
scanf("%d", &m);
for (int i = 0; i < m; i++)
{
int f, t, ww;
scanf("%d %d %d", &f, &t, &ww);
e[f].push_back({ 0, t, ww });
e[t].push_back({ 0, f, ww });
}
scanf("%d", &k);
for (int i = 0; i < k; i++)
{
int f, t, ww;
scanf("%d %d %d", &f, &t, &ww);
e[f].push_back({ 1, t, ww });
e[t].push_back({ 1, f, ww });
}
priority_queue<edge> q;
memset(pre, 0, sizeof(pre));
for (int i = 0; i < 510; i++)
{
dis[i][0] = inf;
dis[i][1] = inf;
}
dis[S][0] = 0;
dis[S][1] = 0;
q.push({ 0, S, 0 });
while (!q.empty())
{
int x = q.top().to;
q.pop();
for (int i = 0; i < e[x].size(); i++)
{
int y = e[x][i].to, ww = e[x][i].w, t = e[x][i].a;
if (!t)
{
if (dis[y][0] > dis[x][0] + ww)
{
zhan pp;
pp.xx = x;
pp.tp = t;
pre[y][0] = pp;
dis[y][0] = dis[x][0] + ww;
q.push({ 0, y, dis[y][0] });
}
if (dis[y][1] > dis[x][1] + ww)
{
zhan pp;
pp.xx = x;
pp.tp = t;
pre[y][1] = pp;
dis[y][1] = dis[x][1] + ww;
q.push({ 1, y, dis[y][1] });
}
}
else
{
if (dis[y][1] > dis[x][0] + ww)
{
zhan pp;
pp.xx = x;
pp.tp = t;
pre[y][1] = pp;
dis[y][1] = dis[x][0] + ww;
q.push({ 1, y, dis[y][1] });
}
}
}
}
cnt = 0;
off = -1;
int ans;
memset(road, 0, sizeof(road));
if (dis[E][0] < dis[E][1])
{
ans = dis[E][0];
find(E, 0);
}
else
{
ans = dis[E][1];
find(E, 1);
}
if (!kk)
kk = 1;
else
printf("\n");
for (int i = 0; i < cnt; i++)
{
if (i != cnt - 1)
printf("%d ", road[i]);
if (i == cnt - 1)
printf("%d\n", road[i]);
}
if (off == -1)
printf("Ticket Not Used\n");
else
printf("%d\n", off);
printf("%d\n", ans);
for (int i = 0; i < 510; i++)
e[i].clear();
}
return 0;
}
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 则输出 ‘?’。
Example 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
Example Output
Case 1:
3
4
Case 2:
?
?
题意:
在图中找出某个节点到剩余每个节点的最小消耗路线。
分析:
该题要求单源最短路径,并且存在负边的情况,只能用SPFA算法。
先将起点到所有点的距离初始化为inf并将起点入队,当队列不为空时,每次取队首元素,将其出队并将label值修改为0。当点出现在队列中的次数大于等于图中的点数时,存在负环,输出“?”,当要缴纳的税小于3时也输出“?”。
代码如下:
#include<iostream>
#include<queue>
#include<algorithm>
#include<cmath>
using namespace std;
const int inf = 1000000000;
struct city
{
int e;
int mo;
};
int T, N, M, Q, P;
int a[210], dis[210], in[210], cnt[210], fu[210], label[210];
void init()
{
for (int i = 0; i < 210; i++)
{
dis[i] = inf;
in[i] = cnt[i] = fu[i] = label[i] = 0;
}
}
void SPFA(vector<city> ro[210])
{
queue<int> q;
dis[1] = 0;
in[1] = 1;
q.push(1);
while (!q.empty())
{
int num = q.front();
q.pop();
in[num] = 0;
for (int i = 0; i < ro[num].size(); i++)
{
int end = ro[num][i].e;
int m = ro[num][i].mo;
if (dis[end] > (dis[num] + m))
{
dis[end] = dis[num] + m;
cnt[end] = cnt[num] + 1;
if (cnt[end] >= N)
{
fu[end] = 1;
queue<int> p;
p.push(end);
label[end] = 1;
while (!p.empty())
{
int ff = p.front();
p.pop();
for (int k = 0; k < ro[ff].size(); k++)
{
int temp = ro[ff][k].e;
fu[temp] = 1;
if (label[temp] == 0)
{
label[temp] = 1;
p.push(temp);
}
}
}
}
if ((in[end] != 1) && fu[end] == 0)
{
q.push(end);
in[end] = 1;
}
}
}
}
}
int main()
{
cin >> T;
int t = T;
while (T--)
{
vector<city> road[210];
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;
city c;
c.e = B;
c.mo = pow((a[B] - a[A]), 3);
road[A].push_back(c);
}
init();
SPFA(road);
cin >> Q;
cout << "Case " << t - T << ":" << endl;
for (int i = 0; i < Q; i++)
{
cin >> P;
if (dis[P] == inf || fu[P] == 1 || dis[P] < 3)
cout << "?" << endl;
else
cout << dis[P] << endl;
}
}
return 0;
}