第7周作业

A - TT 的魔法猫

问题描述

N个人玩一个游戏,每两个人都要进行一场比赛。
已知M个胜负关系,每个关系为A B,表示A比B强,胜负关系具有传递性
试问有多少场比赛的胜负无法预先得知?

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

Output

0
0
4

解题思路

直接使用Floyd–Warshall 算法就可以完成,但要注意合理剪枝,否则会超时。
主要就是a[k][j] =a[k][j]||(a[k][x]&&a[x][j])可以分成两步进行,从而避免一些无效的判断,缩短运行时长。

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

int main()
{
	int m, n, count, n1, n2, num;
	scanf("%d", &count);
	for(int i=0;i<count;i++)
	{
		scanf("%d %d", &n, &m);
		num = 0;
		int a[500][500] = {0};
		for(int j=0;j<m;j++)
		{
			scanf("%d %d", &n1, &n2);
			a[n1][n2] = 1;
		}
		for(int x=1;x<=n;x++)
			for(int k=1;k<=n;k++)
			{
				if(a[k][x])
				{
					for(int j=1;j<=n;j++)
						a[k][j] =a[k][j]||a[x][j];
				}	
			}			
		for(int k=1;k<=n;k++)	
				for(int j=1;j<=n;j++)
					if(!a[k][j]&&!a[j][k]&&k>j)
						num++;				
		printf("%d\n", num);			
	}
	return 0;
}

B - 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),表示可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。
下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
接下来 K 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。

Output

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

本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行**(这个需要额外注意)**

Example

Input

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

Output

1 2 4
2
5

解题思路

思路1
题目给定了起点与终点,而且要求商业线最多乘坐一次。因此可以枚举每一条商业线,计算起点到u的最短路以及v到终点的最短路再加上该商业线所花费的时间。
以起点为源点求单源最短路,得到dis1数组,再以终点为源点求单源最短路,得到dis2数组
枚举商业线(u, v, w),取min{dis1[u]+ dis2[v] +W, dis1[v] +dis2[u]+W},最终再与不走商业线的答案取min,并记录选中的商业线是哪一条。至于路径,通过递归的方式输出。
思路2
将每个车站看成两个车站(0,1),商业线只能在0线或1线上运行,而经济线只能在0线和1线之间运行。因此为了防止多次调用经济线,只需要指定经济线的方向即可,只能从0到1或从1到0,这样即可确保求出的最短路线最多只经过一次经济线。具体路径也是通过递归的方式输出。

代码1

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <vector>
#define inf 5*1e8;
using namespace std;

struct line{
	int x, y, w;
	line(){}	
	line (int a, int b, int c){
		x=a, y=b, w =c;
	}
}; 


int vis[1010], path[1010], dis1[1010], dis2[1010], n, s, e;
priority_queue<pair<int, int> > q;
vector<line> head1[1010], head2;

void dijkstra(int m, int *dis)
{
	while (!q.empty())
		q.pop();
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= n; i++)
		dis[i] = inf;
	dis[m] = 0, path[m] = 0;
	q.push(make_pair(0, m));
	while(!q.empty())
	{
		int x = q.top().second;
		q.pop();
		if(vis[x])
			continue;
		vis[x] = 1;
		for(int i = 0;i<head1[x].size();i++)
		{
			int w=head1[x][i].w, b = head1[x][i].y;
			if(dis[b]>dis[x]+w)
			{
				dis[b] = dis[x]+w;
				if(m==s)
					path[b] = x;
				else
					path[x] = b;
				if((m==s&&b==e)||(m==e&&b==s))
					return;
				q.push(make_pair(-dis[b], b));
			}
		}	
	}
}
void print(int m){
	if(path[m]!=0)
	{
		print(path[m]);
		cout<<m;
		if(m!=e)
			cout<<" ";
	}	
	else
		cout<<m<<" ";
}

int main()
{
	int num, s1, e1, w, l, l1;
	while(~scanf("%d %d %d", &n, &s, &e))
	{
		scanf("%d",&num);
		for(int i=0;i<num;i++)
		{
			scanf("%d %d %d", &s1, &e1, &w);
			line a(s1, e1, w);
			head1[s1].push_back(a);
			line b(e1, s1, w);
			head1[e1].push_back(b);
		}
		dijkstra(e, dis1);
		dijkstra(s, dis2);
		scanf("%d", &num); 
		for(int i=0;i<num;i++)
		{
			scanf("%d %d %d", &s1, &e1, &w);
			line a(s1, e1, w);
			head2.push_back(a);
		}
		s1 = -1, l1 = dis2[e]; 
		for(int i=0;i<head2.size();i++)
		{
			l = min(dis1[head2[i].x] + dis2[head2[i].y] + head2[i].w,
			dis2[head2[i].x] + dis1[head2[i].y] + head2[i].w);
			if(l<l1)
				l1 = l, s1 = i;				
		}
		if(s1>=0)
		{
			if((dis1[head2[s1].x] + dis2[head2[s1].y])<(dis2[head2[s1].x] + dis1[head2[s1].y]))
				path[head2[s1].x] = head2[s1].y, s = head2[s1].y;
			else
				path[head2[s1].y] = head2[s1].x, s = head2[s1].x;
		}	
		print(e);
		printf("\n");
		if(s1>=0)
			printf("%d\n", s);
		else
			printf("Ticket Not Used\n");
		printf("%d\n", l1);
		for(int i=0;i<=n;i++)
			head1[i].clear();	
		head2.clear();
	}
	return 0;
}

代码2

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <vector>
#define inf 5*1e8;
using namespace std;

struct line{
	int x, y, w, kind;
	line(){}	
	line (int a, int b, int c, int d){
		x=a, y=b, w =c, kind = d;
	}
}; 


int vis[1010], path[1010], dis[1010], n, s, e;
priority_queue<pair<int, int> > q;
vector<line> head1[1010];

void dijkstra(int m)
{
	while (!q.empty())
		q.pop();
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= 1010; i++)
		dis[i] = inf;
	dis[m] = 0, path[m] = 0;
	q.push(make_pair(0, m));
	while(!q.empty())
	{
		int x = q.top().second;
		q.pop();
		if(vis[x])
			continue;
		vis[x] = 1;
		for(int i = 0;i<head1[x].size();i++)
		{
			int w=head1[x][i].w, b = head1[x][i].y;
			if(dis[b]>dis[x]+w)
			{
				dis[b] = dis[x]+w;
				path[b] = x;
				q.push(make_pair(-dis[b], b));
			}
		}	
	}
}
void print(int m, int& s1){
	if(path[m]!=0)
	{
		print(path[m], s1);
		if(m>500)
			cout<<m-500;
		else
			cout<<m;
		if(m!=e&&m!=e+500)
			cout<<" ";
	}	
	else
		cout<<m<<" ";
	if(m>500&&path[m]<500)
		s1 = path[m];
}

int main()
{
	int num, s1, e1, w, l, l1;
	int cas = 0;
	while(~scanf("%d %d %d", &n, &s, &e))
	{
		scanf("%d",&num);
		for(int i=0;i<num;i++)
		{
			scanf("%d %d %d", &s1, &e1, &w);
			line a(s1, e1, w, 0);
			head1[s1].push_back(a);
			line b(e1, s1, w, 0);
			head1[e1].push_back(b);
			line c(s1+500, e1+500, w, 0);
			head1[s1+500].push_back(c);
			line d(e1+500, s1+500, w, 0);
			head1[e1+500].push_back(d);
		}
		scanf("%d", &num); 
		for(int i=0;i<num;i++)
		{
			scanf("%d %d %d", &s1, &e1, &w);
			line a(s1, e1+500, w, 0);
			head1[s1].push_back(a);
			line b(e1, s1+500, w, 0);
			head1[e1].push_back(b);
		}
		dijkstra(s);
		if(cas)
			printf("\n");
		else
			cas++;
		if(dis[e]>dis[e+500])
		{
			print(e+500, s1);
			printf("\n");
			printf("%d\n", s1);
		}
		else
		{
			print(e, s1);
			printf("\n");
			printf("Ticket Not Used\n");
		}
		l = min(dis[e], dis[e+500]);
		printf("%d\n", l);
		for(int i=0;i<=1010;i++)
			head1[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

Output

Case 1:
3
4
Case 2:
?
?

解题思路

我们要求的是从1出发到其他点花费的最少的钱,相当于求1到其他点的最短路。
值得注意的一点是,经过一条道路, 收的税可能是负数。因此图中存在负权边,这是一个含负权边的单源最短路径问题,所以需要SPFA算法来解决。
我们只需要找出负环和那些通过负环可以到达的点,这些点的最短路径都不存在(通过dfs即可完成),然后按要求输出即可。

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <cmath>
#include <vector>
#define inf 5*1e8
using namespace std;

struct line{
	int x, y, w;
	line(){}	
	line (int a, int b, int c){
		x=a, y=b, w =c;
	}
}; 

int dis[300], pre[300], inq[300], cnt[300], a[300], n;
vector<line> G[300];
queue<int> q; 

void dfs(int x){
	if(pre[x]==1)
		return;
	pre[x] = 1, inq[x]=1;
	for(int i=0;i<G[x].size();i++)
		dfs(G[x][i].y);	
}

void SPFA(int s)
{
	for (int i = 1; i <= n; ++i)
	{
	    dis[i] = inf;
	    inq[i] = 0;
	    pre[i] = 0;
	    cnt[i] = 0;
	}
	dis[s] = 0;
	inq[s] = 1;
	q.push(s);
	while (!q.empty())
	{
	    int u = q.front();
	    q.pop();
	    inq[u] = 0;
	    if(pre[u]==1)
	    	continue;
	    for (int i=0;i<G[u].size();i++)
	    {
	        int v = G[u][i].y;
	        if (dis[v] > dis[u] + G[u][i].w)
	        {
	            cnt[v] = cnt[u] + 1;
	            if (cnt[v] >= n)
	            	dfs(v);	
                dis[v] = dis[u] + G[u][i].w;
                if (!inq[v])
                {
                    q.push(v);
                    inq[v] = 1;
                }
	        }
	    }
	}
}

int main()
{
	int count1, count2, s, e;
	scanf("%d",&count1);
	for(int i=1;i<=count1;i++)
	{
		scanf("%d", &n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d", &a[i]);
			G[i].clear();
		}	
		scanf("%d", &count2);
		for(int i=0;i<count2;i++)
		{
			scanf("%d%d", &s, &e);
			int value = pow(a[e]-a[s], 3);
			line a(s, e, value);
			G[s].push_back(a);
		}
		SPFA(1);
		scanf("%d", &count2);
		printf("Case %d:\n", i);
		for(int i=0;i<count2;i++)
		{
			scanf("%d", &s);
			if(pre[s] == 1||dis[s]<3||dis[s]==inf)
				printf("?\n");
			else
				printf("%d\n", dis[s]);
		}
	}
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值