Program week7 work

A - TT 的魔法猫 Floyd

众所周知,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能赢b,抽象为一条有向边,注意胜负关系具有传递性,a赢b,b赢c,则a赢c。该题使用Floyd算法。注意Floyd算法k必须在第一层枚举。该传递只有在a[i][k]==1且a[k][j]==1时才更新a[i][j]=1,所以只要二者有一个为0就可以退出判断,即剪枝(此题不剪枝过不了)

Answer

具体思路见注释

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn=1e3+100; 
int num,n,m,a,b,dis[maxn][maxn]={0};

void Floyd(){
	for(int k=1;k<=n;k++)
	    for(int i=1;i<=n;i++)
	        if(dis[i][k])//剪枝 
		        for(int j=1;j<=n;j++)
		            if(dis[k][j])//剪枝 
					dis[i][j]=1;//如果不剪枝,则需要dis[i][j]=max(dis[i][j],dis[i][k]&dis[k][j]);
}

int main()
{
	cin>>num; 
	while(num--){
		cin>>n>>m;
		for(int i=1;i<=m;i++){
			cin>>a>>b;
			dis[a][b]=1;
		}
	Floyd();
	int ans=0;
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++){
	    	if(dis[i][j]==0&&dis[j][i]==0&&i!=j)
	    	ans++;
		}
	cout<<ans/2<<endl;
	memset(dis,0,sizeof(dis));
	}
	
	return 0;
}

B - TT 的旅行日记 dijkstra

众所周知,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算法。
dij1和dij2分别以起点和终点为源点进行遍历。对加入的商业线的处理,这里选择对每条商业线进行遍历,(假设商业线源点和终点分别为u和v,权重为w)所以有两种可能分别为dis1[u]+dis2[v]+w或dis1[v]+dis2[u]+w,取二者的最小值,然后在与不用商业线的路径长度取最小值
此题需要注意的地方:
用链式前向星存储无向图,所以记得add两次 ;
输出从起点s出发的路径需要递归,而输出到终点t的路径只需要递推即可

Answer

具体思路见注释

#include<bits/stdc++.h>
const int N=1e6+100;
const int M=1e6+100;
const int inf=1e8;
using namespace std;

struct Edge{
	int to,next,w;
}e[M];
int head[N],tot,n,m,vis[N],dis1[N],dis2[N];
int pre1[M],pre2[M];
int S,E,X,Y,Z,K;
int ans=inf;

void init(int* a,int n){
	for(int i=0;i<n;i++){
		a[i]=0;
	}
}

void add(int x,int y,int w){
	e[++tot].to=y;
	e[tot].next=head[x];
	e[tot].w=w;
	head[x]=tot;
} 

priority_queue< pair<int,int> >q;
void dijkstra1(int s){
	while(q.size()) q.pop();
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++) dis1[i]=inf;
	dis1[s]=0;
	q.push(make_pair(0,s));
	while(q.size()){
		int x=q.top().second;
		q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x];i;i=e[i].next){
			int y=e[i].to,w=e[i].w;
			if(dis1[y]>dis1[x]+w){
				dis1[y]=dis1[x]+w;
				q.push(make_pair(-dis1[y],y));
				pre1[y]=x;
			}
		}
	}
}

void dijkstra2(int s){
	while(q.size()) q.pop();
	memset(vis,0,sizeof vis);
	for(int i=1;i<=n;i++) dis2[i]=inf;
	dis2[s]=0;
	q.push(make_pair(0,s));
	while(q.size()){
		int x=q.top().second;
		q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x];i;i=e[i].next){
			int y=e[i].to,w=e[i].w;
			if(dis2[y]>dis2[x]+w){
				dis2[y]=dis2[x]+w;
				q.push(make_pair(-dis2[y],y));
				pre2[y]=x;
			}
		}
	}
}

void print1(int source,int t){
	if(t!=source)print1(source,pre1[t]);
	cout<<t<<" ";
}

void print2(int end,int start){
	int s=start;
	while(s!=0&&s!=end){
		printf("%d ",s);
		s=pre2[s];
	}
	printf("%d",s);
}

int main(){
	int flag=0;
	while(scanf("%d %d %d",&n,&S,&E)!=EOF){
		if(flag!=0){printf("\n");}flag=1;
		ans=inf,tot=0;
		init(head,N),init(pre1,M),init(pre2,M);
		scanf("%d",&m);
		for(int i=0;i<m;i++){
			scanf("%d %d %d",&X,&Y,&Z);
			add(X,Y,Z);
			add(Y,X,Z);
		}
		
		dijkstra1(S);
		dijkstra2(E);
		int up=0,down=0;
		scanf("%d",&K);
		for(int i=0;i<K;i++){
			scanf("%d %d %d",&X,&Y,&Z);
			if(dis1[X]+dis2[Y]+Z<ans) {
				ans=dis1[X]+dis2[Y]+Z;
				up=X,down=Y;
			}
			if(dis1[Y]+dis2[X]+Z<ans){
				ans=dis1[Y]+dis2[X]+Z;
				up=Y,down=X;
			}
		}
		if(dis1[E]<ans){
			print2(E,S);
			cout<<endl<<"Ticket Not Used"<<endl;
			cout<<dis1[E]<<endl;
		}
		else{
			print1(S,up);
			print2(E,down);
			cout<<endl<<up<<endl;
			cout<<ans<<endl;
		}

		 
	}
	return 0;
}

C - TT 的美梦 SPFA

这一晚,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:
?
?

思路

本题需要用到SPFA( Bellman-Ford算法 的队列优化算法,单源最短路)算法优点:
1.时间复杂度比普通的Dijkstra和Ford低。
2.能够计算负权图问题。
3.能够判断是否有负环
因为税费的三次方即边权可能是负数,所以可能出现负环。如果出现负环(判断条件为点v的邻接点数cnt>=n,因为一旦出现负环,就会在该环上不断循环下去,所以利用该条件判断是否为负环),就进行dfs将负环上的所有点进行标记
最后满足 未被标记且可以到达以及其他限制条件 的点即为答案

Answer

具体思路见注释

#include<iostream>
#include<math.h>
#include<queue>
#include<string.h>
using namespace std;
const int maxn=1e5+100,N=2e2+10,inf=1e8;
struct Edge{
	int to,next;
}e[maxn];
int n,m,tot,head[N],a[N],dis[N],pre[N],cnt[N],r[N];
int T,x,y,Q;
bool access[N];
queue<int>q;
void add(int x,int y){
	e[++tot].to=y;
	e[tot].next=head[x];
	head[x]=tot;
}
void init(){
	memset(head,-1,sizeof(head)); 
	tot=-1;
	for(int i=0;i<=n;i++){
		cnt[i]=0,dis[i]=inf,pre[i]=-1;
		r[i]=0;
		access[i]=true;
	}
}
void dfs(int s){
	access[s]=false;
	for(int i=head[s];i!=-1;i=e[i].next){
		int u=e[i].to;
		if(access[u])
		dfs(u);
	}
}
void spfa(int s){
	while(!q.empty())q.pop();
	dis[s]=0,pre[s]=s,r[s]=1;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		r[u]=0;
		if(!access[u])continue;
		for(int i=head[u];i!=-1;i=e[i].next){
			int v=e[i].to;
			if(!access[v])continue;
			if(dis[v]>dis[u]+pow((a[v]-a[u]),3)){
				cnt[v]=cnt[u]+1;
				if(cnt[v]>=n){
					dfs(v);
					continue;
				}
				dis[v]=dis[u]+pow((a[v]-a[u]),3);
				pre[v]=u;
				if(!r[v]){
					q.push(v);
					r[v]=1;
				}	
			}
		} 
	}
}
int main() 
{
	scanf("%d",&T);
	for(int j=1;j<=T;j++)
	{
		cin>>n;
		init();
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		cin>>m;
		for(int i=1;i<=m;i++){
			scanf("%d%d",&x,&y);
			add(x,y);
		}
		spfa(1);
		scanf("%d",&Q);
		printf("Case %d:\n",j);
		while(Q--){
			int t;
			scanf("%d",&t);
			if(!access[t]||dis[t]==inf||dis[t]<3)cout<<"?"<<endl;
			else cout<<dis[t]<<endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值