目录
1:插松枝
原题链接:4472. 插松枝 - AcWing题库
永远都是最恶心的模拟题目啊!!!
小插曲:本次天梯赛难度过高,引起了好多高校的老师不满,不仅是题目的问题,还有一部分赛后一些人的发言问题,所以这些题目就当做提高自己能力看吧;
这道题目我们依然一步一步的看,首先存入推送器上的所有松枝,接下来开始插,这里我们考虑某个枝条不能再插松枝的情况,这里我们一定要明白一个优先顺序:一定是先拿篮子里的松枝,篮子里的松枝不符合要求了我们才要拿推送器上的松枝!!!基于此我们开始分析;
我们推送器上的松枝个数是固定的,每一次都要试一下,所以这里我们完全不需要循环,直接遍历推送器上的松枝即可;
大括号内,我们首先要试一试篮子里的松枝,循环进行判断,如果篮子里有松针并且小于上一个松枝,我们就把他加入,这里有个小技巧,我们可以把初始状态的松枝干上的松枝赋值为无穷,这样没有松枝时也有一个具体的值可以让我比较。如果不存在合法的松枝或者没有松枝,直接跳过即可,这里也可能有多个合法松枝,加着加着就超过松枝干的合法长度了,所以你需要加一步判断,如果超过了长度就要重置;
接着判断推送器上的松针是否可以加入,如果不可以加入说明此时推送器,篮子都不符合题意,重置松枝干即可,之后判断推送器上的松枝能不能放入篮子,如果可以就放入,不可以就把循环--,这样下次遍历还是原来的位置。
可能加上推送器上的松枝之后,我们的松枝干达到了最长,这个时候就要重置松枝干;
推送器结束遍历后,我们还有篮子,我们需要再开一个循环遍历篮子里的松枝,只需要上一步不合理的情况变成重置松枝干即可;
代码如下:
#include<iostream>
using namespace std;
int t[1010];
int boo[25],bt=0;//boo是篮子的长度,bt是模拟篮子最高处,这里模拟栈
int main()
{
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>t[i];
int to=0x3f3f3f3f,cnt=0;//to代表松枝干上一个松枝的长度,这里因为没松枝,设置为无穷
for(int i=1;i<=n;i++)
{
while(bt!=0)//首先需要特判篮子,这里是如果篮子为空就退出
{
if(boo[bt]<=to)//篮子里存在可以加入的松枝,直接加入
{
to=boo[bt];
cout<<to<<' ';//直接输出长度即可
bt--;//篮子高度减一
cnt++;//松枝干上的松枝数目
}
else break;//这里是,如果篮子最上方的松枝不符合要求,我们就需要直接退出,去判断推送器
if(cnt==k)//我们发现松枝干插满了,换一个松枝干
{
to=0x3f3f3f3f;//重置为空松枝干
cnt=0;
cout<<endl;//换松枝干了要换行
}
}
if(t[i]<=to)//这里是如果推送器上的松枝可以加入的情况
{
to=t[i];
cout<<to<<' ';
cnt++;
}
else //加入不了的情况,进行特判
{
if(bt==m)//篮子也满了
{
i--;//把i--,意为下次推送器还是这个位置不变,松枝干重置
to=0x3f3f3f3f;
cnt=0;
cout<<endl;
}
else //篮子没满,把松枝加入篮子
{
boo[++bt]=t[i];
}
}
if(cnt==k)//如果推送器上的加入之后发现松枝干满了,也要进行重置
{
to=0x3f3f3f3f;
cnt=0;
cout<<endl;
}
}
while(bt!=0)//推送器结束后,篮子可能没结束,这里还需要特判,结束条件只能是篮子见底
{
if(boo[bt]<=to)//可以加入
{
to=boo[bt];
cout<<to<<' ';
bt--;
cnt++;
}
else//注意这里不能加入松枝的时候,就要把松枝干重置,因为没有推送器了,只在篮子里循环
{
to=0x3f3f3f3f;
cnt=0;
cout<<endl;
}
if(cnt==k)//正常的松枝干满了就重置
{
to=0x3f3f3f3f;
cnt=0;
cout<<endl;
}
}
//因为每一步都是判断着输出着,所以不需要最后输出什么了;
}
2:老板的作息表
这道题目并不难,比赛的时候这道题目也是好多人第一个做出来的L2的题目;
观察了整个题目,其实就是想让我们对所有时间里的空缺处进行查找,然后把空缺处找出来即可,这道题难度并不大;
#include<iostream>
#include<algorithm>
using namespace std;
struct ST
{
int hh1,mm1,ss1;
int hh2,mm2,ss2;
}arr[50010];//用来存a-b时间段
int cmp(ST a,ST b)
{
if(a.hh1!=b.hh1)
return a.hh1<b.hh1;
else if(a.mm1!=b.mm1)
return a.mm1<b.mm1;
else
return a.ss1<b.ss1;
}//自定义结构体排序,因为题目保证不会有时间段重叠,所以可以把第一个时间排序
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%d:%d:%d - %d:%d:%d",&arr[i].hh1,&arr[i].mm1,&arr[i].ss1,&arr[i].hh2,&arr[i].mm2,&arr[i].ss2);
}
sort(arr,arr+n,cmp);
// cout<<endl;
// for(int i=0;i<n;i++)
// {
// printf("%d:%d:%d - %d:%d:%d\n",arr[i].hh1,arr[i].mm1,arr[i].ss1,arr[i].hh2,arr[i].mm2,arr[i].ss2);
// }
// cout<<endl;
int h=0,m=0,s=0;//这里是凌晨时分,因为我们要判断的是上一个时间段的最后与下一个时间段的开始;
//所以这里我们直接从零点开始
for(int i=0;i<n;i++)
{
if(arr[i].hh1!=h||arr[i].mm1!=m||arr[i].ss1!=s)
{
//只要上个时间的末尾和这个时间的开始存在一点不一样,就需要进行输出,这里判断注意不要写成&&
printf("%02d:%02d:%02d - %02d:%02d:%02d\n",h,m,s,arr[i].hh1,arr[i].mm1,arr[i].ss1);
}
h=arr[i].hh2,m=arr[i].mm2,s=arr[i].ss2;//把这个时间段的末尾赋值,这样方便下一个的判断
// cout<<h<<' '<<m<<' '<<s<<endl;
}
if(h!=23||m!=59||s!=59)//最后需要特判一下23:59:59,因为题目是不会给我们特判的
{
printf("%02d:%02d:%02d - 23:59:59\n",h,m,s);
}
}
3:龙龙送外卖
这道题我也属于马后炮,考试场上根本不会,写完题目现在回顾才发现其实并不难(当初考场上怎么没发现呢!该打)
这道题目的意思是,我们会有n个点的树,然后每次把某个点加入送餐序列,你需要找到一条最短路径把食物送完,但是这里我们不需要回到开始的地方,最后一句话是个关键,因为如果需要回到最开始的地方,其实答案就不止一种了;
看一个简陋的图,这里我们把5,6,8设置为送餐地点,我们有6种遍历方法,明显先遍历某个子树更优,这样我们就有三种更优的方法,但是答案其实都是一样的,因为我们回到了起点;
但是这里的题目没有要求我们必须回到起点,所以我们就可以想办法在回去的路上减少时间,我们很容易发现,6-5-8这条路线是最短的,因为从8-1回去的路径长达3其他两家只有二,我们可以明白,6-5-8是最优路径,这里的最优路径不止一条,我们也要多方面考虑;
做法是,我们可以先考虑走完全程需要多少路径,走完全程的路径长度是我们的起点到达每一个点的长度乘以二(因为要回到起点)减去最后送餐的那家店回起点的长度,这样我们就可以计算了;
使用并查集来存储树,再开一个数组记录他到达起点的长度,这样每次加入某个点,我们只需要比较他和已经加入的点到达起点的路径长度谁更长,谁更长就让谁作为最后一个送餐点,因为这样肯定是最优的;
代码如下:
#include<iostream>
using namespace std;
int arr[200010],du[200010];//arr存储树结构,du存储这个点到达起点的长度
int sum=0;//sum是总长度,送完所有餐到达起点的长度(这里我们不需要考虑送餐顺序)
//因为会有多个解都是最优的,我们只需要找到离起点最长的那个点的长度即可;
int dfs(int a)
{
if(arr[a]==-1||du[a]>0) return du[a];// 如果我们发现这个点是原点(距离始终为0)
//或者我们到达了一个已经计算过的点的位置,
sum++;//这里的计算是不会少的,我们需要计算出每次的送餐点到最近的送餐路线的位置
du[a]=dfs(arr[a])+1;//递归它上面的点,从它上面的点继续找
return du[a];//把这个点到起点的长度返回
}
int main()
{
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>arr[i];
int mx;//离起点最远的点的长度
while(q--)
{
int a;
cin>>a;
mx=max(mx,dfs(a));//进行判断,找到新加入点后,离原起点最远的那个点是否发生了变化
cout<<sum*2-mx<<endl;//输出总长度减去最后到达的送餐点不回去的路程就是我们的答案
}
}
4:大众情人
这道题即使看过题解我还是懵了好长时间,不过清醒过后会发现其实并不困难;
题目意思是,每个男生对于其他人都有一个距离,这个距离可以被重新计算,取决于他和另一个人的中间人之间的距离如果小于他和那个人的距离即可更新,eg
不管a-c的距离是多少,只要a-b+b-c小于a-c那么前者才是真正的距离,所以这里我们就需要计算所有人之间的距离,要求的是找到一个男/女,他的所有异性朋友中,和他距离最远的异性导数最大(其实说白了就是和她距离最小),这样的男/女不止一个,我们需要找到所有的;
当计算完所有人的距离之后,我们遍历所有男生,对于每个男生,找到距离最远的女生的距离进行更新最短距离,这样我们就能得到一个最短距离,但是可能不止一个男生有这个距离,所以我们需要逐个输出;
女生同理;
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510,INF=0x3f3f3f3f;
int d[N][N];
char sex[N];
int main()
{
int n;
cin>>n;
memset(d,INF,sizeof(d));//首先所有人都是最远距离
for(int i=1;i<=n;i++) d[i][i]=0;//自己和自己的距离为0
for(int i=1;i<=n;i++)
{
cin>>sex[i];//这里要记录是男生还是女生,因为两者需要分别输出
int q;
cin>>q;
while(q--)
{
int p,dis;
scanf("%d:%d",&p,&dis);
d[i][p]=dis;//输入距离
}
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);//计算矩阵所有人到其他人最短距离用floyed
for(auto c:string("FM"))//遍历男女
{
int mn=INF;//最短距离 的女生
for(int i=1;i<=n;i++)
if(sex[i]==c)//假设先遍历女,首先性别是个女生
{
int mx=0;//这个女生的所有异性,和他距离最远的那个异性的距离
for(int j=1;j<=n;j++)//遍历
if(sex[i]!=sex[j])//如果不是女生(就是男生)这是个判断
mx=max(mx,d[j][i]);//进行更新,找到与这个女生距离最远的男生的距离
mn=min(mn,mx);//进行更新,我们只需要最大距离最小的女生
}
for(int i=1;i<=n;i++)
if(sex[i]==c)
{
int mx=0;
for(int j=1;j<=n;j++)
if(sex[i]!=sex[j])
mx=max(mx,d[j][i]);
if(mx==mn)//查找上文计算的那个女生 (可能不止一个)
{
cout<<i<<' ';
}
}
cout<<endl;//输出后遍历男生
}
}
总感觉有点中央空调的感觉,a和b同性,但是和a最疏远的人关系比和b最疏远的人关系好,a就是大众情人(和异性关系都好),有点中央空调那味了...
啊对了忘说了,y总估计是把数据给暗改了,所以输出末尾空格也没事,如果想要不输出的话,可以开个数组存储答案,然后有条件输出即可(我还是觉得特判空格很麻烦!!!)
完结撒花