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

A - TT 的魔法猫

题目描述

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

这一天,TT 正在专心致志地玩《猫和老鼠》游戏,然而比赛还没开始,聪明的魔法猫便告诉了 TT 比赛的最终结果。TT 非常诧异,不仅诧异于他的小猫咪居然会说话,更诧异于这可爱的小不点为何有如此魔力?

魔法猫告诉 TT,它其实拥有一张游戏胜负表,上面有 N 个人以及 M 个胜负关系,每个胜负关系为 A B,表示 A 能胜过 B,且胜负关系具有传递性。即 A 胜过 B,B 胜过 C,则 A 也能胜过 C。

TT 不相信他的小猫咪什么比赛都能预测,因此他想知道有多少对选手的胜负无法预先得知,你能帮帮他吗?

  • 第一行给出数据组数。

每组数据第一行给出 N 和 M(N , M <= 500)。

接下来 M 行,每行给出 A B,表示 A 可以胜过 B。

  1. 对于每一组数据,判断有多少场比赛的胜负不能预先得知。注意 (a, b) 与 (b, a) 等价,即每一个二元组只被计算一次。
  2. input
    3
    3 3
    1 2
    1 3
    2 3
    3 2
    1 2
    2 3
    4 2
    1 2
    3 4
  3. output
    0
    0
    4

分析

  1. 这道题所有的猫猫构成一个图,猫的胜负关系构成一个有向边,可以计算所有有向边的闭包,并且用数组记录,使用弗洛伊德算法,将所有的路径得出结果
  2. 单纯使用弗洛伊德算法由于o(n^3)的复杂度,会超时,所以我们要考虑剪枝,思考在弗洛伊德算法当中,当第一次枚举,如果如果对于i和k这条边没有有向边,则下一个枚举点j就没有必要了,因为永远不可能修改j,k边,

代码

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=5E2+10;
int gra[maxn][maxn];
int m,n,tmp;
int main(){
	int num,op1,op2;scanf("%d",&num);
	while(num--){
		int ans=0;
		memset(gra,0,sizeof(gra));
		scanf("%d%d",&n,&m);
		for(int i=0;i<m;i++){
			scanf("%d%d",&op1,&op2);
			gra[op1][op2]=1;
			gra[op2][op1]=-1;
		}
		for(int k=1;k<=n;k++){
			for(int i=1;i<n;++i){
				if(i==k||!gra[i][k]) continue;
				for(int j=i+1;j<=n;j++){
					if(j==k) continue;
					if(!gra[i][j]&&(gra[i][k]*gra[k][j])>0){
						gra[i][j]=gra[i][k];gra[j][i]=-gra[i][k];
						//printf("%d,%d\n",i,j);
					}
				}
			}
		}
		for(int i=1;i<n;i++){
			for(int j=i+1;j<=n;j++)
				if(!gra[i][j]) ++ans;
		}
		printf("%d\n",ans);
	}
}

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 前往喵星机场花费的总时间。
    本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行

  • 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. 这道题我是采用第一个思路,即列举从头和尾进行迪杰科斯拉算法,计算所有的点到两头的最短距离,然后,一一列举所有的特殊路径(a,b),计算所有的路径的头和尾的距离之和和反制的距离之和,取最小的并且记录,计算所有的特殊路径的最短长度,和不使用的最短路径比较就可以
  2. 对于路径的求取,是使用两个数组分别记录在dij算法中的走向,之后需要的时候,从尾部开始遍历到头,记录所有经过的节点就可以

代码

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int nmax=1E3+10;
const int mmax=2E3+10;
const int inf=1E9;
//链式前向星
struct edge{
	int to,next,w;
}e1[mmax];
int head[nmax],tot=1;
void add(int x,int y,int w){
	e1[++tot].to=y,e1[tot].w=w;
	e1[tot].next=head[x],head[x]=tot;
}
//
int dis1[nmax],dis2[nmax],vis[nmax],pre1[nmax],pre2[nmax];
int n,s,e,edge1,edge2;
void init(int d[],int pre[]){for(int i=0;i<=n;i++){d[i]=inf,vis[i]=0,pre[i]=0;}}
void dij(int st,int dis[],int pre[]){
	init(dis,pre);
	priority_queue<pair<int,int> > que;
	que.push(make_pair(0,st));dis[st]=0;
	while(!que.empty()){
		int x=que.top().second;que.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x];i;i=e1[i].next){
			if(!vis[e1[i].to]&&dis[e1[i].to]>dis[x]+e1[i].w){
				pre[e1[i].to]=x;
				dis[e1[i].to]=dis[x]+e1[i].w;
				que.push(make_pair(-dis[e1[i].to],e1[i].to));
			}
		}
	}
}
void output(int pre[],int pos,int end){
	if(pos==end) {printf("%d ",pos);return ;}
	output(pre,pre[pos],end);
	printf("%d ",pos);
}
int main(){
        bool flag=true;
	while(~scanf("%d%d%d",&n,&s,&e)){
                if(!flag) printf("\n");
                else flag=false;
 		memset(head,0,sizeof(head));
		tot=1;
		scanf("%d",&edge1);
		for(int i=0;i<edge1;++i){int a,b,c;scanf("%d%d%d",&a,&b,&c);add(a,b,c);add(b,a,c);}
		dij(s,dis1,pre1);
		dij(e,dis2,pre2);
		scanf("%d",&edge2);
		int now=dis1[e],b1,b2;
	//for(int i=1;i<=n;i++) printf("%d  ",dis1[i]);
		for(int i=0;i<edge2;++i){
			int a,b,c;scanf("%d%d%d",&a,&b,&c);
			if(dis1[a]+dis2[b]>dis1[b]+dis2[a]) swap(a,b);
			if(dis1[a]+dis2[b]+c<now){
				b1=a,b2=b;
				now=dis1[a]+dis2[b]+c;
			}
		}
	//printf("|%d|\n",now);
		if(now==dis1[e]){
			vector<int> res;
			int pos=s;
			while(pos){
				res.push_back(pos);
				pos=pre2[pos];
			}
			for(int i=0;i<res.size();i++) printf(i==0?"%d":" %d",res[i]);
			printf("\nTicket Not Used\n%d\n",now);
		}
		else {
			/*output(pre1,b1,s);
			int pos=b2;
			while(pos!=e){
				printf("%d ",pos);
				pos=pre2[pos];
			}
			printf("%d\n",e);*/
			int pos=b1;
			vector<int> res;
			while(pos){
				res.push_back(pos);
				pos=pre1[pos];
			}
			reverse(res.begin(),res.end());
			pos=b2;
			while(pos){
				res.push_back(pos);
				pos=pre2[pos];
			}
			for(int i=0;i<res.size();i++) printf(i==0?"%d":" %d",res[i]);
			printf("\n%d\n%d\n",b1,now);
		}
	}
	return 0;
}

C - TT 的美梦

题目描述

这一晚,TT 做了个美梦!

在梦中,TT 的愿望成真了,他成为了喵星的统领!喵星上有 N 个商业城市,编号 1 ~ N,其中 1 号城市是 TT 所在的城市,即首都。

喵星上共有 M 条有向道路供商业城市相互往来。但是随着喵星商业的日渐繁荣,有些道路变得非常拥挤。正在 TT 为之苦恼之时,他的魔法小猫咪提出了一个解决方案!TT 欣然接受并针对该方案颁布了一项新的政策。

具体政策如下:对每一个商业城市标记一个正整数,表示其繁荣程度,当每一只喵沿道路从一个商业城市走到另一个商业城市时,TT 都会收取它们(目的地繁荣程度 - 出发地繁荣程度)^ 3 的税。

TT 打算测试一下这项政策是否合理,因此他想知道从首都出发,走到其他城市至少要交多少的税,如果总金额小于 3 或者无法到达请悄咪咪地打出 ‘?’。

  • 第一行输入 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 号点的最少税费。
  • 每个询问输出一行,如果不可达或税费小于 3 则输出 ‘?’。
  • 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:
    ?
    ?

题意分析

这道题就是考察最短路径的题目,并且题中有负环的可能,所以要使用spfa算法来进行处理使用cnt数组来记录到某个点的路径的长度,如果使用长度超过了最长的长度,就证明出现了负环,那么负环上的点,以及负环能够到达的所有的点,我们都不能够计算他们的长度,所以我们就给他们标记上负无穷的可到长度使用bfs来搜索他到的点,得到所有的路径的距离,根据题目给的输出格式进行输出就可以了

代码

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int nmax=2E2+10;
const int inf=1E9;
const int NO=1E9+2;
vector<pair<int,int> > edge[nmax];
int a[nmax],inq[nmax],dis[nmax],cnt[nmax];
int n,m,q,op1,op2;
void init(){for(int i=1;i<=n;++i){inq[i]=0,cnt[i]=0;dis[i]=inf;}}
int get_p(int x,int y){ int t=a[x]-a[y];return t*t*t;}
/*void dfs(int ss){
	if(dis[ss]==0) return ;
	dis[ss]=0;
	for(auto iter=edge[ss].begin();iter!=edge[ss].end();iter++){
		dfs(iter->second);
	}
}*/
void bfs(int ss){
	queue<int> quu;
	if(dis[ss]==-inf) return;
	quu.push(ss);
	while(!quu.empty()){
		int x=quu.front();quu.pop();
		for(auto iter=edge[x].begin();iter!=edge[x].end();++iter){
			if(dis[iter->second]!=-inf){
				dis[iter->second]=-inf;
				quu.push(iter->second);
			}
		}
	}
}
void spfa(){
	queue<int> que;
	init();
	que.push(1);
	inq[1]=1,dis[1]=0;
	while(!que.empty()){
		int x=que.front();que.pop();inq[x]=0;
		for(auto iter=edge[x].begin();iter!=edge[x].end();iter++){
			int wei=get_p(iter->second,x);
			if(dis[iter->second]>dis[x]+wei){
				cnt[iter->second]=cnt[x]+1;
				if(cnt[iter->second]>=n) {bfs(x);continue;}
				dis[iter->second]=dis[x]+wei;
				if(!inq[iter->second]){ inq[iter->second]=1;que.push(iter->second);}
			}
		}
	}
}
int main(){
	int num;scanf("%d",&num);
	for(int p=1;p<=num;p++){
		scanf("%d",&n);
		for(int i=1;i<=n;++i) {scanf("%d",a+i);edge[i].clear();}
		scanf("%d",&m);
		for(int i=1;i<=m;i++) {scanf("%d%d",&op1,&op2);edge[op1].push_back(make_pair(op1,op2));}
		spfa();
		printf("Case %d:\n",p);
		scanf("%d",&q);
		
		for(int i=0;i<q;i++){
			int tmp;scanf("%d",&tmp);
			if(dis[tmp]<3||dis[tmp]==inf) printf("?\n");
			else printf("%d\n",dis[tmp]);
		}
			}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值