华东交通大学2022年ACM“双基“程序设计竞赛

Fly

1.铁路

题意:
n个点,m条路将其连起来。
q次询问,
opt=1时输出A,B两点的最短距离;
opt=2时在A,B之间修建一条花费时间C的铁路。

思路:
先无向边建图,
注意:可能有重边和自环,
因此,如果是自环,d应该取0;
如果有重边,应取其中w最小的。
首先做一次Floyd算法,先求出任意两点之间的最短距离。
然后对于每次询问,
opt=1直接输出。
opt=2时,如果f[a][b]>c,更新f[a][b]和f[b][a],
并做一次Floyd算法。

代码:

include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N = 210;
int n,m,q,f[N][N],opt,a,b,c;

void floyd(){
	for(int k=1;k<=n;k++){//k为中间点 
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
			}
		}
	}
}

int main(){
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j) f[i][j]=0;
		    else f[i][j]=INF;
		}
	}
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		f[x][y]=f[y][x]=min(f[x][y],z);
	}
	floyd();//首先做一遍floyd 
	while(q--){
		scanf("%d%d%d",&opt,&a,&b);
		if(opt==1){//直接输出 
			printf("%d\n",f[a][b]);
		}
		else{
			scanf("%d",&c);
			if(f[a][b]>c){//如果大于
			//更新并做一遍floyd 
				f[a][b]=f[b][a]=min(f[a][b],c);
				floyd();
			}
		}
	}
	return 0;
}
 

2.n层妖塔

题意:
m个挑战者,n层楼。当挑战者的战力>第i层楼的守护者的战力时,
花费1个单位时间将其击败,击败第i层之后可以前往第i+1层,
当战胜第n层的守护者后,视为挑战成功。
当挑战者的战力<=第i层楼的守护者的战力时,可以花费相应时间修炼获得战力,
当获得a[i]+1的战力之后,就可以打败这个守护者了。
计算每一位挑战者挑战成功所需要花费的最短时间。

思路:
先预处理出每一层至少需要多少战力才能毫无阻碍地挑战成功。
因为最终要战胜第n层的守护者之后,才视为挑战成功。
设f[i]表示第i层至少需要的战力,
那么由于第n层是终结层,因此f[n]=a[n]+1;
然后开始从第n层递推到第1层。
对于每一层:要么取a[i]+1,
要么因为每次前进,所以后一层会对前一层有限制,
因此要么就是f[i+1]-1,-1是除去打败这一层所需的时间。
f[i]=max(a[i]+1,f[i+1]-1);

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int n,m,a[N],f[N];

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	f[n]=a[n]+1;
	for(int i=n;i>=1;i--){
		f[i]=max(a[i]+1,f[i+1]-1); 
	} 
	while(m--){
		int x,y;
		scanf("%d%d",&x,&y);
		if(x>=f[y]) printf("%d\n",n-y+1);//仅为每次打败所需时间 
		else printf("%d\n",n-y+1+(f[y]-x));//还要加上修炼的时间 
	}
	return 0;
}

3.火车摆放

题意:
n个火车,对于任意i属于[2,n],第i号火车只能连在第i-1号火车的后面。
m次操作,
1.(1,x):如果第x1号火车存在,则接在第x1号火车后面,
如果第x+1号火车也存在,则将第x+1号火车接在第x号火车后面),
如果第x号火车已经被摆放了,则忽略该操作。
2.(2,x)表示将第x号火车拆下来,如果第x号火车已经被拆掉了,则忽略该操作。
3.(3,x)询问以第x号火车为车头的连续列车长度是多少。

思路:
本来想的是利用set,因为set具有自动去重并排序的功能。
1操作就将x加入集合,
2操作就将x从集合中删除,
3操作就在集合中找x的最长连续长度。
但是这样第3个操作会遍历很久,时间复杂度很高。
因此可以逆向思维,
先将1-n加入set集合,
opt=1,删除x;
opt=2,添加x;
opt=3,再进行计算。
因为此时s中存放的是被拆除的火车,即断点。
首先特判,如果这个火车已经被拆除,则输出0;
否则,
当查询以x号火车为车头的连续列车长度时,
只需要去set集合中找到>=x的第一个元素的火车(亦即位置),
如果 相减<0,表示后面一串都行;
否则,输出输出这个 位置-这个火车的位置。
这样的话3操作可以利用STL的lower_bound找到>=x的第一个数,
直接计算最长连续长度。

set小知识

(1)set::lower_bound()是C++
STL中的内置函数,该函数返回指向容器中元素的迭代器,该迭代器等效于在参数中传递的k。如果set容器中不存在k,则该函数返回一个迭代器,该迭代器指向刚好大于k的下一个元素。如果传递给参数的键超过了容器中的最大值,则返回的迭代器将指向设置容器中的最后一个元素。
(2)//删除 set 容器中值为 val 的元素 size_type erase (const value_type& val);
//删除 position 迭代器指向的元素 iterator erase (const_iterator position); //删除
[first,last) 区间内的所有元素 iterator erase (const_iterator first,
const_iterator last);

代码:

#include <bits/stdc++.h>
using namespace std;
int n,m,opt,x;
set<int> s;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) s.insert(i);
	while(m--){
		scanf("%d%d",&opt,&x);
		if(opt==1) s.erase(x);
		else if(opt==2) s.insert(x);
		else{
			if(*s.lower_bound(x)-x<0) printf("%d\n",n-x+1);
			else printf("%d\n",*s.lower_bound(x)-x);
		}
	}
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值