程序设计思维与实践 Week7 作业 (3/4/数据班)

1.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

思路:利用矩阵a[][]表示边,ans储存可预知的数量,若a[1][2]=1则表示1与2的比赛结果可预知。输入完成后矩阵中一部分值为1,ans有一个不为0的值.从矩阵左上角开始遍历整个矩阵,若a[i][k]=1则表示i到k胜负可知,此时遍历所有与i相连的点j,若a[k][j]=1,则易知i与j胜负可知,则ans++,
在这里插入图片描述遍历好所有点后,只需用N*(N-1)/2即总场数减去ans即可得到不可预知的场数。

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
struct edg{
	int u,v;
};
int main(){
	int n,m,N;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d%d",&N,&m);
		int a[N+2][N+2];
		for(int i=1;i<=N;i++)
		for(int l=1;l<=N;l++){
			a[i][l]=0;
		}
		int ans=0;
		for(int i=1;i<=m;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			a[u][v]=1;
			ans++;
		}
		for(int k=1;k<=N;k++){
			for(int i=1;i<=N;i++){
				if(a[i][k]!=0){
					for(int j=1;j<=N;j++){
				     if(a[i][j]==1) continue;
			         int b=a[i][k]&a[k][j];
			         a[i][j]=max(a[i][j],b);
			         if(a[i][j]==1) ans++;
		}
				}
			}
		}
		ans=N*(N-1)/2-ans;
		cout<<ans<<endl;
	}
	
	
	return 0;
} 

2.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

思路:用Dijkstra方法分别构造从起点开始以及从终点开始的最短路dis1,、dis2,对于每一个商业线A到B,计算从起点到A的长度加上终点到B的长度加上A到B的长度即可的到一个解,再计算B到A的上述过程得另一解,计算完所有商业线后选出最小解再与不经过商业线到达终点的长度比较,小者即为所需要的解。

#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
struct Edge{
	int u,p;
	bool operator<(const Edge& a){
		if(this->p<a.p) return true;
		return false;
	}
};
struct sy{
	int u,v,p;
};
void Dijkstra(int s,int e,int n,int dis[],int par[],vector<Edge> jingji[]){
	int v[1010]={0};       //判断是否经过 
	priority_queue<pair<int,int>> g; //最大堆 
	dis[s]=0;
	g.push(make_pair(0,s));
	while(!g.empty()){
		int x=g.top().second;
		g.pop();
		if(v[x]==1) continue;
		v[x]=1;
		for(unsigned i=0;i<jingji[x].size();i++){           //遍历与x相连的点,更新dis[] 
			if(dis[jingji[x][i].u]>dis[x]+jingji[x][i].p){
				dis[jingji[x][i].u]=dis[x]+jingji[x][i].p;
				par[jingji[x][i].u]=x;
				g.push(make_pair(-dis[jingji[x][i].u],jingji[x][i].u));
			}
		}
	}
}
int main(){
	int N,S,E,M,K,u,v,p;
	int k=0; 
	while(scanf("%d%d%d",&N,&S,&E)!=EOF){
		if(k==1) printf("\n");
		vector<Edge> jingji[1000+10];
        vector<sy> shangye;
		int dis1[1010];
        int dis2[1010];
        int p1[1010],p2[1010];
		for(int i=1;i<=N;i++){
		dis1[i]=1e6;
		dis2[i]=1e6;
		p1[i]=i;
		p2[i]=i;
	}
	scanf("%d",&M);
	for(int i=1;i<=M;i++){
		scanf("%d%d%d",&u,&v,&p);
		jingji[u].push_back({v,p});
		jingji[v].push_back({u,p});
	}
	scanf("%d",&K);
	for(int i=1;i<=K;i++){
		scanf("%d%d%d",&u,&v,&p);
		shangye.push_back({u,v,p});
	}
	int mi=1e7,a,b,c,d,e,f,h,ff,hh;
    Dijkstra(S,E,N,dis1,p1,jingji);
	Dijkstra(E,S,N,dis2,p2,jingji);
	for(unsigned i=0;i<shangye.size();i++){
		a=dis1[shangye[i].u];
		b=dis2[shangye[i].u];
		c=dis1[shangye[i].v];
		d=dis2[shangye[i].v];
		if(a+d+shangye[i].p<b+c+shangye[i].p){
			e=a+d+shangye[i].p;
			f=shangye[i].u;
			ff=shangye[i].v;
		}
		else{
			e=b+c+shangye[i].p;
			f=shangye[i].v;
			ff=shangye[i].u;
		}
		if(e<mi){
			mi=e;
			h=f;
			hh=ff;
		}
	}
	int q=dis1[E];
	if(q<mi){
		int y=S;
		while(p2[y]!=y){
			printf("%d ",y);
			y=p2[y];
		}
		printf("%d\n",y);
		printf("Ticket Not Used\n");
		mi=q;
	}
	else {
		int Y=h;
		vector<int> gg; 
		while(p1[Y]!=Y){
			gg.push_back(Y);
			Y=p1[Y];
		}
		gg.push_back(Y);
		for(int i=gg.size()-1;i>=0;i--){
			printf("%d ",gg[i]);
		}
		int y=hh;
		while(p2[y]!=y){
			printf("%d ",y);
			y=p2[y];
		}
		printf("%d\n",y);
		printf("%d\n",h);
	}
	printf("%d\n",mi);
	k=1;
	}
	
	return 0;
} 

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

思路:采用链式前向星方式储存边,使用cnt储存每个点的最短路径长度,dis储存从点1到每个点所收取的最小“税收”,从点1开始,将点1放入堆中,之后取出1,遍历与点1相连的所有点,若dis[x]>dis[1]+v(v为1到x的边的权值),则x的cnt值等于1的cnt值加1,若x的cnt大于等于点的个数,则利用dfs方法遍历x能够到达的点,将这些点的dis值设为inf(最大值),并标记为已读;若不大于,则更新x的dis,若x未被标记已读,则将x加入堆并将x标记为已读,重复上述过程指导队列为空即可得到结果。

#include <iostream>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
const int inf=1e6;
struct edge{
	int to,next;
}G[inf];
int T,N,M,A,B,Q,P,tol,a[inf],head[inf],dis[inf],vis[inf],cnt[inf];
void chushi(){
	fill(a,a+N+10,0);
	fill(head,head+N+10,0);
	fill(dis,dis+N+10,inf);
	fill(vis,vis+N+10,0);
	fill(cnt,cnt+N+10,0);
	tol=0;
}
void putin(int from,int to){
	G[++tol].next=head[from];
	G[tol].to=to;
	head[from]=tol;
}
void dfs(int x){
	vis[x]=1;
	dis[x]=inf;
	for(int i=head[x];i!=0;i=G[i].next){
		if(!vis[G[i].to]) dfs(G[i].to);
	}
}
void spfa(){
	queue<int> g;
	int x;
	g.push(1);
	vis[1]=1;
	dis[1]=0;
	while(!g.empty()){
		x=g.front();
		vis[x]=0;
		g.pop();
		for(int i=head[x];i!=0;i=G[i].next){
			int p=G[i].to;
			int y=pow(a[p]-a[x],3);
			if(dis[p]>dis[x]+y){
				cnt[p]=cnp[x]+1;
				if(cnt[p]>=N){
					dfs(p);
					continue;
				}
				dis[p]=dis[x]+y;
				if(!vis[p]){
					g.push(p);
					vis[p]=1;
				}
			}
		}
	}
}

int main(){
	cin>>T;
for(int t=1;t<=T;t++){
	cin>>N;
	chushi();
    for(int i=1;i<=N;i++)
	cin>>a[i];
	cin>>M;
	for(int i=1;i<=M;i++){
		cin>>A>>B;
		putin(A,B);
	}	
	spfa();
	cin>>Q;
	cout<<"Case "<<t<<":"<<endl;
	for(int i=1;i<=Q;i++){
		cin>>P;
		if(dis[P]==inf||dis[P]<3) cout<<"?"<<endl;
		else cout<<dis[P]<<endl;
	}
}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值