程序设计作业week07

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) 等价,即每一个二元组只被计算一次。

Sample Input

3
3 3
1 2
1 3
2 3
3 2
1 2
2 3
4 2
1 2
3 4

Sample Output

0
0
4

解题思路

胜负关系具有传递性,因此可以用 Floyd 算法求出
任意两点的胜负关系(传递闭包),即可求出答案。
• dis[a][b] = 1 表示 a 比 b 强
• dis[a][b] = 0 表示 a 与 b 的胜负关系不明
• dis[a][b] = 0 且 dis[b][a] = 0 即表示 a 与 b 的胜负关系无法预先判断

Floyd算法

这个题目里要改变一下形式,不再是存距离最小值,而是保留传递关系,1表示传递,0表示没有传递关系,所以取最大值,有1则保留传递性。

dis[ii][jj] = max(dis[ii][jj], dis[ii][kk] && dis[kk][jj]);

剪枝:当 dis[i][k] = 0 时,i - k - j 一定不具有传递性,所以由原有 dis[i][j] 决定,不用进行下面的判断。

if(dis[ii][kk] == 0) continue;

完整代码

#include <iostream>
#include <string.h>
#include <algorithm>
#include <cmath>
using namespace std;
int k, n, m;
int a, b;
bool dis[510][510];
void floyd()
{
	for(int kk=1;kk<=n;kk++)
		for(int ii=1;ii<=n;ii++)
		{
			if(dis[ii][kk] == 0) continue;
			for(int jj=1;jj<=n;jj++)
				dis[ii][jj] = max(dis[ii][jj], dis[ii][kk] && dis[kk][jj]);
		}
}
int main()
{
	cin>>k;
	for(int i=0;i<k;i++)
	{
		cin>>n>>m;
		memset(dis, 0, sizeof(dis));
		for(int j=0;j<m;j++)
		{
			cin>>a>>b;
			dis[a][b] = 1;
		}
		floyd();
		int cnt = 0;
		for(int ii=1;ii<=n;ii++)
		{
			for(int jj=ii+1;jj<=n;jj++)
			{
				if(dis[ii][jj]==0 && dis[jj][ii]==0)
				cnt++;
			}
		}
		cout<<cnt<<endl;
	}
	return 0;
}

B - TT 的旅行日记

题目

众所周知,TT 有一只魔法猫。

今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!
输入

输入

包含多组数据。每组数据第一行为 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 行是商业线路段的描述,格式同经济线。

所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。

输出

对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点),第二行是 TT 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行是 TT 前往喵星机场花费的总时间。

本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行

输入样例

4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3

输出样例

1 2 4
2
5

题解思路

这是一个求最短路的题目,但是增加了商业线的约束。要求输出符合要求的最短路长度,记录的最短路路径,以及记录的换乘点。

• 跑一次单源最短路(变形),记录答案 dis[u][0/1]
• dis[u][0] 表示从起点到结点 u 没有经过商业线时的最短
路,在松弛的时候可以选择商业线或者经济线
• dis[u][1] 表示从起点到结点 u 经过商业线后的最短路,
在松弛的时候只能选择经济线

松弛操作:当遍历的边是经济线时,分别对 dis[u][0/1] 进行松弛操作;当该边是商业线时,用未使用经济线的 dis[u][0] 对 dis[u][1] 进行松弛操作。
if(dis[y][0]>dis[x][0]+ww) //没有用过商业线的 + 经济线 = 经济线

if(!t)      //经济线 
            {
                if(dis[y][0]>dis[x][0]+ww)  //没有用过商业线的 + 经济线 = 经济线 
                {
                   //
                }
                if(dis[y][1]>dis[x][1]+ww)  //用过商业线的 + 经济线 = 商业线 
                {
                   //
                }
            }
            if(t)       //商业线 
            {
                if(dis[y][1]>dis[x][0]+ww)  //只能是没有用过商业线的 
                {
                   //
                }
            }   

路径输出:在松弛操作时顺便记录前驱节点pre,然后用递归倒推用数组记录路径,然后输出。

注意:严格按照输出格式,不能有任何多余的空格空行,两组数据之间有空行,末尾没有。

完整代码

#include <iostream>
#include <vector>
#include <queue>
#include <cstdio>
#include <cmath>
#include <string.h>
using namespace std;
int n, S, E;
int m, k, cnt = 0, off = -1;
int dis[510][2];

int road[510];
struct pre_p
{
    int xx, tp;
};
pre_p pre[510][2];
struct edge
{
    bool type;  //0是经济线 1是商业线 
    int to, w;
    bool operator<(const edge &ee)const
    {
        return w > ee.w;
    }
};
vector<edge> e[510];
void make_graph()
{
    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});
    }
}
void dijkstra(int s)
{
    priority_queue<edge> q;
    memset(pre, 0, sizeof(pre));
    for(int i=0;i<510;i++)
    {
        dis[i][0] = 100000000;
        dis[i][1] = 100000000;
    }
    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].type;
            if(!t)      //经济线 
            {
                if(dis[y][0]>dis[x][0]+ww)  //没有用过商业线的 + 经济线 = 经济线 
                {
                    pre_p 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)  //用过商业线的 + 经济线 = 商业线 
                {
                    pre_p 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]});
                }
            }
            if(t)       //商业线 
            {
                if(dis[y][1]>dis[x][0]+ww)  //只能是没有用过商业线的 
                {
                    pre_p 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]});
                }
            }   
        }
    }   
    
}
void find(int end, int p)
{
    if(end == S)
    {
        road[cnt] = S;
        cnt++;
        return;
    }
    pre_p 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);
        make_graph();
        dijkstra(S);
        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 则输出 ‘?’。

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:
?
?

题解思路

含有负权环路的单源最短路问题,Floyd多源算法时间复杂度高,dijkstra无法处理负权的情况,所以使用SPFA。

SPFA

• 第一轮,与 S 邻接的点被松弛 -> 最短路径上的第一条边
• 第二轮,与第一轮被松弛的点相邻接的点被松弛 -> 最短路径上的第二条边

这样我们不妨每次只做有效的松弛操作

• 建立一个队列
• 队列中存储被成功松弛的点
• 每次从队首取点并松弛其邻接点
• 如果邻接点成功松弛则将其放入队列

负权环路:判断点的入队次数,如果某一点入队 n 次则说明有负环,然后以该点为起点遍历,找到所有联通的点打上标记。

完整代码

#include <iostream>
#include <cmath>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
struct edge
{
	int from, to, w;
};
int T, N, M, Q;
int a[210];
int dis[210];
int cnt[210];
int inq[210];
int vis[210];
vector<edge> e[210];
void make_graph(int m)
{
    for(int i=0;i<m;i++)
    {
        int f, t, ww;
        cin>>f>>t;
        ww = pow(a[t] - a[f], 3);
        e[f].push_back({f, t, ww});		//有向图 
    }
}
void tag_ring(int ss)
{
    queue<int> q;
    q.push(ss); 
	vis[ss] = 1;
    while(!q.empty())
    {
        int x = q.front(); 
		q.pop();
        for (int i=0;i<e[x].size();i++)
        {
            int y = e[x][i].to;
            if(!vis[y])
			{
                q.push(y);
                vis[y] = 1;
            }
        }
    }
}
void SPFA(int s)
{
	for(int i=1;i<=N;i++)
	{
		dis[i] = 10000000;
		cnt[i] = 0;
		inq[i] = 0;
		vis[i] = 0;
	}
	queue<int> q;
	dis[s] = 0;
	inq[s] = 1;
	q.push(s);
	while(!q.empty())
	{
		int u = q.front();
		q.pop();
		inq[u] = 0;		//现在不在队列里了 
		for(int i=0;i<e[u].size();i++)
		{
			int v = e[u][i].to;
			if(dis[v] > dis[u] + e[u][i].w)
			{
				cnt[v] = cnt[u] + 1;
				if(cnt[v] >= N)
				{
					tag_ring(v);
				} 
				dis[v] = dis[u] + e[u][i].w;
				if(!inq[v] && !vis[v])
				{
					q.push(v);
					inq[v] = 1;
				}
			}
		}
	}
}
int main()
{
	cin>>T;
	int k = 1;
	while(T--)
	{
		memset(a, 0, sizeof(a));
		cin>>N;
		for(int i=1;i<=N;i++)	cin>>a[i];
		cin>>M;
		make_graph(M);
		cin>>Q;
		SPFA(1);
		cout<<"Case "<<k<<':'<<endl;
		k++;
		while(Q--)
		{
			int p;
			cin>>p;
			if(vis[p] || dis[p] < 3 || dis[p] == 10000000) 	cout<<'?'<<endl;
            else cout<<dis[p]<<endl;
		}
		for(int i=0;i<210;i++)	e[i].clear();
	}
	return 0;
} 
发布了10 篇原创文章 · 获赞 3 · 访问量 750

猜你喜欢

转载自blog.csdn.net/wakeupshely/article/details/105372686
今日推荐