【考题题解6】稍复杂的模拟 栈+dfs 单调队列

本文解析了四道编程题目,包括模拟游戏中的生存者、食堂座位安排、括号表达式的值计算以及解决旅行中的加油问题。通过具体算法和代码实现,展示了如何运用数据结构和算法解决问题。

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

1.无聊的游戏

由题意可得,这道题只需要村模拟即可,就是稍微复杂里一点

1.循环枚举每一个没死的同学,找到与该同学最近的一个非自身且没有死的同学,然后将其杀掉(标记一下)即可。

2.因为给的同学的位置是坐标系的坐标,所以求最近距离是要用上勾股定理,即行列的绝对值之差的平方之和。

3.如果一次循环没有把剩下的杀死,那就再循环一次即可。

4.如果在模拟的过程中总数为1,那么就找到唯一生存的同学并且输出以及退出循环。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int students;
struct Cristiano_Ronaldo
{
	int x,y,die;
}student[3000]={};//x,y记录坐标;die记录该学生是否死掉
inline int find_the_shortest_student(int student_number)//找到与第student_number个学生最近的学生
{
	double shortest_distance=999999999.0;
	int shortest_student_number=0;
	for (int i=1;i<=students;i++)
	    if (i!=student_number&&student[i].die==0)//学过没有死并且不是自己本身
	    {
	    	double distance=sqrt(pow(abs(student[student_number].x-student[i].x),2)
			                  +pow(abs(student[student_number].y-student[i].y),2));
			if (shortest_distance>distance)
			{
				shortest_distance=distance;
				shortest_student_number=i;
			}//勾股定理求最小值
	    }
	return shortest_student_number;
}
inline int find_the_only_one_student()//找到唯一生存的学生
{
	for (int i=1;i<=students;i++)
	if (student[i].die==0) return i;
}
int main()
{
	cin>>students;
	if (students==0) 
	{
		cout<<0;
		return 0;
	}
	for (int i=1;i<=students;i++)
	    cin>>student[i].x>>student[i].y;
	for (int i=1;i<=3000;i++) 
	    student[i].die=0;//标记未死亡
	int sum=students;
	while (true)
	{
		for (int i=1;i<=students;i++)
		{
			if (sum==1)//如果只剩下一个学生
			{
				cout<<find_the_only_one_student();
				return 0;
			}
			if (sum>1&&student[i].die==0)//如果该学生没死并且总数大于1
			{
				int willdie=find_the_shortest_student(i);//找到最近的学生
				student[willdie].die=1;//杀死
				sum--;
			}
		}
		if (sum==1)
		{
			cout<<find_the_only_one_student();//找到唯一生存的学生
			return 0;
		}
	}
	return 0;
}

2.食堂的座位

模拟条件:

1.如果该座位是单人的,那么优先考虑左半部分,避免对后面产生影响,如果没有再考虑右边

2.如果是双人座位的左边,那么就判断左边有没有位置;如果是双人座位的右边,那么就判断座位的右边有没有位置。

3.因为数据较少,用二维数组uesd[i][j]来表示i和j之间的位置是否被坐。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int seats,used[100][100]={},double_seat=-1,ans=0;
	char seat[1000]={};
	cin>>seats;
	for (int i=1;i<=seats;i++) 
	    cin>>seat[i];
	for (int i=1;i<=seats;i++)
	{
		if (seat[i]=='S') 
		{
			if (used[i-1][i]==0) used[i-1][i]=1;
			    else if (used[i][i+1]==0) used[i][i+1]=1;
			        else ans++;
		}//判断是单人座位的情况
		if (seat[i]=='L')
		{
			double_seat=(double_seat+1)%2;//该变量控制的是双人座位的方向,即左边或右边
	    	if (double_seat==0) 
     		    if (used[i-1][i]==0) used[i-1][i]=1;
    		       else ans++;
    		else
	    	    if (used[i][i+1]==0) used[i][i+1]=1;
	    	        else ans++;
	    }//判断双人座位的情况
	}
	cout<<seats-ans;
	return 0;
}

3.括号表达式的值

1.因为由题意可得,括号是平衡的,因此可以用括号匹配来做,找出与每一个左括号所匹配的右括号。

2.括号匹配的方法如下:用栈记录左括号的地址,每次匹配到一个右括号就记录栈顶所匹配的右括号的地址,并出栈,如此反复操作即可。

3.接着,就可以dfs了,先dfs(1,n),然后接着递归就可以。

递归过程如下:

a.如果它由多个平衡的括号组成,就递归第一个平衡的括号和剩下部分。

b.如果有且只有一对不嵌套的括号,返回值为2.

c.如果只有一对平衡且带有嵌套的括号,去掉头和尾接着递归,并且结果乘2即可。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const long long Mod=12345678910;
struct Cristiano_Ronaldo
{
	int num,next;//num记录0和1,next记录匹配的右括号
}a[100000]={};
long long dfs(long long l,long long r)//dfs递归
{
	long long ans=0;
	if (a[l].next<r) ans=(ans+((dfs(l,a[l].next)%Mod)+(dfs(a[l].next+1,r)%Mod)%Mod))%Mod;//上述条件a
	if (l+1==r) ans=(ans+1)%Mod;//上述条件b
	if (a[l].next==r&&l+1<r) ans=(ans+2*(dfs(l+1,r-1)%Mod)%Mod)%Mod;//上述条件c
	return ans;
}
int main()
{
	long long n,top=0,st[100000]={};
	cin>>n;
	for (int i=1;i<=n;i++)cin>>a[i].num;
	for (int i=1;i<=n;i++)
	{
		if (a[i].num==1) a[st[top]].next=i,top--;
		if (a[i].num==0) st[++top]=i;
	}//进行括号匹配
	cout<<dfs(1,n);
	return 0;
}

4.加油问题

1.因为数据没有排序,应按照地点进行排序。

2.排序后,要使加油费最小,用单调队列来维护每一个加油站的加油费和每一个加油站作加的油,令最便宜的油在对头即可。

3.因为要使车能达到终点,没到一个站都要把油加满,如果加的油比队尾的油要便宜,那么就把队尾去掉,直到前面的油比它大或者队列为空即可。

4.每次要用油的时候就从对头取油用,因为是最便宜的。

5.因为一开始已经拥有的油是不用钱的,说宜价格标记为0即可。

Q:这么反复将油加满不会对结果产生影响吗?因为加了很多没必要的油啊,那么结果不是会更高吗?

A:不会,因为队列只是用来控制可用的且最便宜的油的数量,最后用多少要看你在对头去多少油,因此给加满油的油不一定会用完,而是取多少用多少,最终的结果也只是和你取得油量有关。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int head=1,tail=1,ans=0;
struct jgt
{
	int x,y;
}a[100000]={};
bool cmp(jgt xx,jgt yy)
{
	return (xx.x<yy.x);
}
struct que
{
	int p,h;//p=price,h=have
}q[100000]={}; 
void go(int x)
{
	int s=x;
	while (true)
	{
		if (s<q[head].h)//如果距离小于存油量 
		{
			q[head].h-=s;//减少存油量 
			ans+=q[head].p*s;// 将用去的油乘这个油的价格存入总花费 
			return;
		}
		else//如果大于存油量 
		{
			s-=q[head].h;//当该油量用完时 总路程也减少 
			ans+=q[head].p*q[head].h;//算花费 
			head++;//去对头 
		}
	}
} 
int pre_sum(int h,int t)//查找h到t区间的油量之和 
{
	int sum=0;
	for (int i=h;i<=t;i++)
	    sum+=q[i].h;
	return sum;
}
int main()
{
	int n,g,b,d;
	cin>>n>>g>>b>>d;
	for (int i=1;i<=n;i++)
	    cin>>a[i].x>>a[i].y;
	sort(a+1,a+n+1,cmp); //按照地点排序 
	q[1].p=0;
	q[1].h=b;//把原有油进队 
	for (int i=1;i<=n;i++)
	{
		if (g<a[i].x-a[i-1].x)//如果两点间的距离比所能存储的容量大的话无解 
		{
			cout<<-1;
			return 0;
		}
		go(a[i].x-a[i-1].x);//走到下一个点,括号内表示走的距离 
		while (a[i].y<q[tail].p&&head<=tail) 
		    tail--;//如果要新加入的元素的价格比队尾要小,切队列不为空 
		q[++tail].p=a[i].y;//储存价格 
		q[tail].h=g-pre_sum(head,tail-1);//储存所能存储的油量 
	}
	if (g<d-a[n].x)
	    cout<<-1;
    else
        go(d-a[n].x);//从最后一个点走到终点 
	cout<<ans;
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值