程序设计思维与实践 Week7 作业

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

思路:

根据题目易知,该题是需要求传递闭包。考虑到这里的数据范围非常小,N,M<=500。因此考虑利用Floyd算法的性质来求闭包。先把每一组给出的胜负关系当成图中的一条边,图中各个点为选手,若a战胜b则图中有一条a指向b的边。两点之间只要有边则说明该比赛结果已知。图初始化好了以后,直接使用变形的Floyd即可,所谓变形就是改变其迭代条件,由
dis [ i ][ j ] = min( dis [ i ][ j ], dis [ i ][ k ] + dis [ k ][ j ] )
变为
dis [ j ][ k ] = dis [ j ][ k ] || dis [ j ][ i ] && dis[ i ][ k ]。
待Floyd执行完毕后,统计图中没有边的点的二元组的个数即可。

代码:

#include <stdio.h>
int win[510][510];
int main()
{
	int o;
	scanf("%d",&o);
	for(int v=0;v<o;v++)
	{
		int n,m;
		scanf("%d %d",&n,&m);
		for(int i=0;i<m;i++)
		{
			int a,b;
			scanf("%d %d",&a,&b);
			win[a][b]=1;
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(win[j][i]==0)
					continue;
				for(int k=1;k<=n;k++)
				{
					win[j][k]=win[j][k]||win[j][i]&&win[i][k];
				}
			}
		}
		int count=0;
		for(int i=1;i<n;i++)
			for(int j=i+1;j<=n;j++)
			{
				if(win[i][j]==0&&win[j][i]==0)
					count++;
			}
		printf("%d\n",count);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				win[i][j]=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 前往喵星机场花费的总时间。

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

思路:
这一题如果没有商业线的话那就是单纯的求单源最短路径。因为出现了商业线,因此该题主要有两个思路:1、因为只能乘一条商业线,所以可以枚举商业线,然后求起点到该商业线的距离,商业线到终点的距离,两者加上商业线长度后求得路线总长度,求出最小值,与不经过商业线比最小的即为最短长度。2、采用分层图。可将原图看成一个三维的图,图中只能经过一次捷径,因此原图分为两层,两层都为经济线构成的图,连接两层对应点的为商业线。对该分层图直接跑计算单源最短路算法即可。
这里我采用的是第二种思路,同时使用的是spfa算法,采用记录某一节点上一节点的方式来记录路径,商业线的换乘只要记录换层时的节点即可。

代码:

#include <stdio.h>
#include <string.h> 
#include <queue>
#include <vector>
#include <stack>
using namespace std;
#define inf 100000
struct node
{
	int num;
	int weight;
	int flag;
	node(int n,int w,int f):num(n),weight(w),flag(f){
	}
	node(){
		
	}
};
vector<node> map[1010];
int dis[510][2];
int vis[510][2];
int pre[510][2];
queue<int> q;
void init(int s)
{
	for(int i=0;i<1010;i++)
		map[i].clear();
	memset(dis,1,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(pre,0,sizeof(pre));
	dis[s][0]=0;dis[s][1]=0;
}
int main()
{
	int n,s,e,m,k;
	int first=0;
	while(scanf("%d %d %d",&n,&s,&e)!=EOF)
	{
		if(first!=0)
		{
			printf("\n");
		}
		first=1;
		init(s);
		scanf("%d",&m);
		for(int i=0;i<m;i++)
		{
			int x,y,z;
			scanf("%d %d %d",&x,&y,&z);
			node tmp;
			tmp.num=y;tmp.weight=z;tmp.flag=0;
			map[x].push_back(tmp);
			tmp.num=x;
			map[y].push_back(tmp);
			tmp.flag=1;
			map[y+n].push_back(tmp);
			tmp.num=y;
			map[x+n].push_back(tmp);
		}
		scanf("%d",&k);
		for(int i=0;i<k;i++)
		{
			int x,y,z;
			scanf("%d %d %d",&x,&y,&z);
			node tmp;
			tmp.num=y;tmp.weight=z;tmp.flag=1;
			map[x].push_back(tmp);
			tmp.num=x;
			map[y].push_back(tmp);
		}
		
		q.push(s);
		while(!q.empty())
		{
			int f=q.front();
			q.pop();
			int fflag=0;
			if(f>n)
				fflag=1;
			vis[f-fflag*n][fflag]=0;
			for(int i=0;i<map[f].size();i++)
			{
				int onum=map[f][i].num;
				int oflag=map[f][i].flag;
				int oweight=map[f][i].weight;
				if(dis[onum][oflag]>dis[f-fflag*n][fflag]+oweight)
				{
					dis[onum][oflag]=dis[f-fflag*n][fflag]+oweight;
					pre[onum][oflag]=f;
					if(vis[onum][oflag]==0)
					{
						q.push(map[f][i].num+map[f][i].flag*n);
						vis[onum][oflag]=1;
					}
				}	
			}
		}
		
		stack<int> path;
		int j=e;int judge=1;int change=0;
		if(dis[e][0]>dis[e][1])
			j=e+n;
		while(j!=s)
		{
			if(j<=n)
			{
				if(judge==1)
					change=j;
				judge=0;
				path.push(pre[j][0]);
				j=pre[j][0];
			}
			else
			{
				if(pre[j-n][1]>n)
					path.push(pre[j-n][1]-n);
				else
					path.push(pre[j-n][1]);
				j=pre[j-n][1];
			}
		}
		if(change==0)
			change=s;
		while(!path.empty())
		{
			printf("%d ",path.top());
			path.pop();
		}
		printf("%d\n",e);
		if(dis[e][0]<=dis[e][1])
			printf("Ticket Not Used\n");
		else
			printf("%d\n",change);
		if(dis[e][0]<dis[e][1])
			printf("%d\n",dis[e][0]);
		else
			printf("%d\n",dis[e][1]);
	}
} 

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 则输出 '?'

思路:
该题很显然需要在一个带负权甚至带负环的图中求单源最短路径,因此我们无法使用dijktra来求最短路。这里我们使用spfa来求最短路径,但是为了避免负环的影响,我们需要在spfa中加入对负环的判断。这里我采用点的入队次数来判断是否有负环。因为最短路中边数最多为n-1,所以,若一条边入队n次,必定有负环。但因为spfa是点入队列,不容易判断边,因此这里放宽条件采用点入队n次来判断负环。判定负环后,遍历通过该负环可到达的点,打上标记。最后根据标记和dis数组数出对应结果即可。

代码:

#include <stdio.h>
#include <string.h>
#include <queue>
#include <vector>
#define inf 16843009
using namespace std;
int map[210][210];
int dot[210];
int dis[210];
int vis[210];
int cnt[210];
int mark[210];
void initial()
{
	memset(map,1,sizeof(map));
	memset(dot,0,sizeof(dot));
	memset(dis,1,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	memset(mark,0,sizeof(mark));
	dis[1]=0;
	cnt[1]=1;
}

void dfs(int a,int n)
{
	if(vis[a]==0||mark[a]==0)
	{
		vis[a]=1;
		mark[a]=1;
		for(int i=1;i<=n;i++)
		{
			if(map[a][i]!=inf)
			{
				dfs(i,n);
			}
		}
	}
}

void spfa(int n)
{
	queue<int> q;
	vis[1]=1;
	q.push(1);
	while(!q.empty())
	{
		int f=q.front();
		q.pop();
		vis[f]=0;
		for(int i=1;i<=200;i++)
		{
			if(map[f][i]!=inf)
			{
				if(dis[i]>dis[f]+map[f][i])
				{
					dis[i]=dis[f]+map[f][i];
					if(cnt[i]>n)
					{
						dfs(i,n);
					}
					if(vis[i]==0)
					{
						q.push(i);
						vis[i]=1;
						cnt[i]++;
					}
				}
			}
		}
	}
}
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("data.out","w",stdout);
	int t;
	scanf("%d",&t);
	for(int i=0;i<t;i++)
	{
		initial();
		int n,m,q;
		scanf("%d",&n);
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&dot[j]);
		}
		scanf("%d",&m);
		for(int j=1;j<=m;j++)
		{
			int a,b;
			scanf("%d %d",&a,&b);
			map[a][b]=(dot[b]-dot[a])*(dot[b]-dot[a])*(dot[b]-dot[a]);
		}
		spfa(n);
		for(int j=1;j<=n;j++)
		{
			if(mark[j]==1)
			{
				dis[j]=-1;
			}
		}
		scanf("%d",&q);
		printf("Case %d:\n",i+1);
		for(int j=0;j<q;j++)
		{
			int p;
			scanf("%d",&p);
			if(dis[p]==-1||dis[p]<3||dis[p]==inf)
				printf("?\n");
			else
				printf("%d\n",dis[p]);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值