2022团体程序设计天梯赛题解 L2

这篇博客介绍了四个算法题目,涉及栈、队列、有根树和最短路径问题。第一题是关于栈和队列的操作,第二题处理区间查询,第三题求有根树的最短路径,第四题涉及图中节点间的最短距离。博主通过模拟和Floyd算法解决了这些问题,并分析了时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目
感觉今年L2有点难,难在题目描述,题目描述的云里雾里的比题目本身难得多。2-3我确实没想出来,确实菜,不过想出来也拿不了国二,太菜啦,抓紧remake叭。

L2-1
题意: 题意很烦。给定一个栈,栈容量为m;一个数组,长度为n;需要填进若干个队列里,每个队列最大容量为k。按照什么规则呢?如果栈不为空,就从栈顶取,如果栈的元素不满足条件,就从数组取,如果数组的元素还不满足条件,就把他放入栈,如果栈已满,则不入栈,改为判断下一个队列。如果栈为空,从数组取。如果满足条件,则放入当前队列;否则,将数组元素压入栈。当满足以下三个条件之一时,换下一个队列。
1.队列已满
2.栈已满而且数组元素不满足条件
3.栈顶元素不满足条件但是数组元素已取完。
思路: 模拟,有点无语。
时间复杂度: O(n)
代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<complex>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<unordered_map>
#include<list>
#include<set>
#include<queue>
#include<stack>
#define OldTomato ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
#define fir(i,a,b) for(int i=a;i<=b;++i) 
#define mem(a,x) memset(a,x,sizeof(a))
#define p_ priority_queue
// round() 四舍五入 ceil() 向上取整 floor() 向下取整
// lower_bound(a.begin(),a.end(),tmp,greater<ll>()) 第一个小于等于的
// #define int long long //QAQ
using namespace std;
typedef complex<double> CP;
typedef pair<int,int> PII;
typedef long long ll;
// typedef __int128 it;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll inf = 1e18;
const int N = 2e5+10;
const int M = 1e6+10;
const int mod = 1e9+7;
const double eps = 1e-6;
inline int lowbit(int x){ return x&(-x);}
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
int n,m,k,T;
int a[N];
int st[N],top = 0;
vector<int>va[N];
int t = 0,now = 0;
void solve()
{
   cin>>n>>m>>k;
   for(int i=0;i<n;++i) cin>>a[i];
   while(top||now<n)
   {
   	  if(top)
   	  {
   	  	if(!va[t].size()||st[top-1]<=va[t].back()) va[t].push_back(st[--top]);
   	  	else if(now<n)
   	  	{
   	  		if(!va[t].size()||a[now]<=va[t].back()) va[t].push_back(a[now++]);
   	  		else if(top<m)st[top++] = a[now++];
   	  		else t++;
   	  	}
   	  	else t++;
   	  }
   	  else if(now<n)
   	  {
   	  	if(!va[t].size()||a[now]<=va[t].back()) va[t].push_back(a[now++]);
   	  	else if(top<m)st[top++] = a[now++];
   	  	else t++;
   	  }
   	  if(va[t].size()==k) t++;
   }
   for(int i=0;i<=t+1;++i)
   {
   	  if(!va[i].size()) continue;
   	  for(int j=0;j<va[i].size();++j)
   	  {
   	  	if(j) cout<<' ';
   	  	cout<<va[i][j];
   	  }
   	  cout<<"\n";
   }
}
signed main(void)
{  
   T = 1;
   // OldTomato; cin>>T;
   // read(T);
   while(T--)
   {
   	 solve();
   }
   return 0;
}

L2-2
题意: 这个简单,给定n段时间段[l,r],都在同一天,输出不在这些时间段出现的时间段。保证区间合法、不重复出现。不过给的是字符串,要换成数字,然后放到数组里赋值即可。要注意的是,存在时间为0。而且这个区间应该是左闭右开的,端点的判断有点迷,不过过样例了基本就没问题。
思路: 模拟。
时间复杂度: O(246060)
代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<complex>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<unordered_map>
#include<list>
#include<set>
#include<queue>
#include<stack>
#define OldTomato ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
#define fir(i,a,b) for(int i=a;i<=b;++i) 
#define mem(a,x) memset(a,x,sizeof(a))
#define p_ priority_queue
// round() 四舍五入 ceil() 向上取整 floor() 向下取整
// lower_bound(a.begin(),a.end(),tmp,greater<ll>()) 第一个小于等于的
// #define int long long //QAQ
using namespace std;
typedef complex<double> CP;
typedef pair<int,int> PII;
typedef long long ll;
// typedef __int128 it;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll inf = 1e18;
const int N = 2e5+10;
const int M = 1e6+10;
const int mod = 1e9+7;
const double eps = 1e-6;
inline int lowbit(int x){ return x&(-x);}
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
int n,m,k,T;
bool vis[24*60*60+60*60+60];
int fun(string s)
{
	int res = 0,x = 0;
	for(int i=0;i<2;++i) x = x*10+s[i]-'0';
	res += x*60*60; x = 0;
	for(int i=3;i<5;++i) x = x*10+s[i]-'0';
	res += x*60; x = 0;
	for(int i=6;i<8;++i) x = x*10+s[i]-'0';
	res += x; x = 0;
	return res;
}
void fun2(int x)
{
	int h = x/(60*60); x-=h*60*60;
	int min = x/60; x -= min*60;
	int se = x;
	string s;
	printf("%02d:%02d:%02d",h,min,se);
}
void solve()
{
   cin>>n;
   for(int i=0;i<n;++i)
   {
   	  string a,b; cin>>a>>b>>b;
   	  int l = fun(a);
   	  int r = fun(b)-1;
   	  for(int i=l;i<=r;++i) vis[i] = 1;
   }
   m = 23*60*60+59*60+59;
   for(int i=0;i<m;++i)
   {
   	  if(vis[i]) continue;
   	  int j = i;
   	  while(j<m&&!vis[j]) j++;
   	  fun2(i);
   	  cout<<" - ";
   	  fun2(j);
   	  cout<<"\n";
   	  i = j;
   }
}
signed main(void)
{  
   T = 1;
   // OldTomato; cin>>T;
   // read(T);
   while(T--)
   {
   	 solve();
   }
   return 0;
}

L2-3
题意: 给定n个点的有根树。m组查询,从树根出发,求到达m个点各一遍需要走的路径的最小值。(最后不必要回到树根)
思路: 假设我们每次都要回到树根,那么就很好求,深度2即可。那么现在不必要回到树根,但是每个点也需要往上走,只不过可能走不到树根。标记一下走过的点,每个点的额外代价是从它走到走过的点需要走几条边,再2即可。而且有一个点是无需往上走的,即深度最深的链只需要走一次,无需*2,用总代价-最大的深度即是答案。
时间复杂度: O(n)
代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<complex>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<unordered_map>
#include<list>
#include<set>
#include<queue>
#include<stack>
#define OldTomato ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
#define fir(i,a,b) for(int i=a;i<=b;++i) 
#define mem(a,x) memset(a,x,sizeof(a))
#define p_ priority_queue
// round() 四舍五入 ceil() 向上取整 floor() 向下取整
// lower_bound(a.begin(),a.end(),tmp,greater<ll>()) 第一个小于等于的
// #define int long long //QAQ
using namespace std;
typedef complex<double> CP;
typedef pair<int,int> PII;
typedef long long ll;
// typedef __int128 it;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll inf = 1e18;
const int N = 2e5+10;
const int M = 1e6+10;
const int mod = 1e9+7;
const double eps = 1e-6;
inline int lowbit(int x){ return x&(-x);}
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
int n,m,k,T;
vector<int> va[N];
int fa[N];
int d[N];
int mx = 0;
ll ans = 0;
int root;
bool vis[N];
void dfs(int cur)
{
	for(int j:va[cur])
	{
		d[j] = d[cur] + 1;
		dfs(j);
	}
}
void solve()
{
   cin>>n>>m;
   for(int i=1;i<=n;++i) 
   {
   	  int x; cin>>x;
   	  if(x==-1) root = i;
   	  else va[x].push_back(i),fa[i] = x;
   }
   dfs(root);
   while(m--)
   {
   	  int x; cin>>x;
   	  mx = max(mx,d[x]);
   	  for(;x!=root&&!vis[x];vis[x] = 1,x=fa[x],ans+=2);
   	  cout<<ans-mx<<"\n";
   }
}
signed main(void)
{  
   T = 1;
   // OldTomato; cin>>T;
   // read(T);
   while(T--)
   {
   	 solve();
   }
   return 0;
}

L2-4
题意: 题意也是长不拉几的,简单来说是每个点有男性和女性之分。先用数组记录每个点与异性节点的最大的最短路mx[i],求出来以后分别找男性和女性的最小的mx[i],所有等于这个值的点都要输出。(n<=500)
我刚开始没仔细读题,傻了,i的异性缘是别人对他的,我一直用的他对别人的,所以一直过不了样例。然后反着建图就过了,光这个地方就浪费了20分钟时间,没事,不影响结果,这次摔的痛了下次才知道注意。太菜咧QAQ
思路: floyd
时间复杂度: O(n^3)
代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<complex>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<unordered_map>
#include<list>
#include<set>
#include<queue>
#include<stack>
#define OldTomato ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
#define fir(i,a,b) for(int i=a;i<=b;++i) 
#define mem(a,x) memset(a,x,sizeof(a))
#define p_ priority_queue
// round() 四舍五入 ceil() 向上取整 floor() 向下取整
// lower_bound(a.begin(),a.end(),tmp,greater<ll>()) 第一个小于等于的
// #define int long long //QAQ
using namespace std;
typedef complex<double> CP;
typedef pair<int,int> PII;
typedef long long ll;
// typedef __int128 it;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll inf = 1e18;
const int N = 502;
const int M = 1e6+10;
const int mod = 1e9+7;
const double eps = 1e-6;
inline int lowbit(int x){ return x&(-x);}
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
int n,m,k,T;
int g[N];
int a[N][N];
int mn1 = INF,mn2 = INF;
int mx[N];
void floyd()
{
	fir(k,1,n)
	fir(i,1,n)
	fir(j,1,n)
	a[i][j] = min(a[i][j],a[i][k]+a[k][j]);
}
void solve()
{
   cin>>n;
   fir(i,1,n)
   fir(j,1,n)
   if(i!=j) a[i][j] = INF;
   for(int i=1;i<=n;++i)
   {
   	  char ch; cin>>ch;
   	  if(ch=='M') g[i] = 1;
   	  int num; cin>>num;
   	  while(num--)
   	  {
   	  	 int x,z;
   	  	 scanf("%d:%d",&x,&z);
   	  	 a[x][i] = z;
   	  }
   }
   floyd();
   for(int i=1;i<=n;++i)
   {
   	  for(int j=1;j<=n;++j)
   	  {
   	  	 if(g[i]+g[j]==1) mx[i] = max(mx[i],a[i][j]);
   	  }
   	  if(g[i]==0) mn1 = min(mn1,mx[i]);
   	  else mn2 = min(mn2,mx[i]); 
   }
   vector<int> va;
   for(int i=1;i<=n;++i) if(!g[i]&&mx[i]==mn1) va.push_back(i);
   for(int i=0;i<va.size();++i) {if(i)cout<<' '; cout<<va[i];} printf("\n");
   va.clear();
   for(int i=1;i<=n;++i) if(g[i]&&mx[i]==mn2) va.push_back(i);
   for(int i=0;i<va.size();++i) {if(i)cout<<' '; cout<<va[i];} 
}
signed main(void)
{  
   T = 1;
   // OldTomato; cin>>T;
   // read(T);
   while(T--)
   {
   	 solve();
   }
   return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值