Contest3403 - 2024寒假集训 进阶训练赛 (十四)

问题 A: [蓝桥杯2022初赛] 数位排序

题目描述

小蓝对一个数的数位之和很感兴趣,今天他要按照数位之和给数排序。
当两个数各个数位之和不同时,将数位和较小的排在前面,当数位之和相等时,将数值小的排在前面。
例如,2022 排在 409 前面,因为2022 的数位之和是6,小于 409 的数位之和13。
又如,6 排在 2022 前面,因为它们的数位之和相同,而 6 小于 2022。
给定正整数n,m,请问对 1 到 n 采用这种方法排序时,排在第 m 个的元素是多少?

样例输入 
13
5
样例输出 
3
思路分析 

将前n个数按位数和排序,输出第m个即可

AC代码
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
//求位数和 
int sw(int x)
{
	int su=0;
	while(x)
	{
		su+=x%10;
		x/=10;
	}
	return su;
}
//排序规则 
bool cmp(int x,int y)
{
	if(sw(x)==sw(y))
	return x<y;
	return sw(x)<sw(y);
}
int main()
{
	int m,n;
	scanf("%d%d",&m,&n);
	vector<int>ve;
	for(int i=1;i<=m;i++)
	ve.push_back(i);
	sort(ve.begin(),ve.end(),cmp);
	printf("%d\n",ve[n-1]);
}

问题 B: [蓝桥杯2018初赛]全球变暖 

题目描述

你有一张某海域NxN像素的照片,"."表示海洋、"#"表示陆地,如下所示:

.......

.##....

.##....

....##.

..####.

...###.

.......

其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。  
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。
具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。  
例如上图中的海域未来会变成如下样子:

.......

.......

.......

.......

....#..

.......

.......

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

样例输入 
7 
.......
.##....
.##....
....##.
..####.
...###.
....... 
样例输出 
1
思路分析 

用队列存储每一片岛屿,每次取出队头元素,存放与队头元素相邻的岛屿像素块,计算每片岛屿的像素块个数,同时判断该岛屿中每个像素块是否临海,若临海像素块等于该岛屿总像素块,则该岛屿会被淹没

AC代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue> 
#include<string.h>
using namespace std;
char c[1005][1005];
int vis[1005][1005]={0};//记录是否遍历过 
//上下左右方位 
int xx[4]={0,0,1,-1};
int yy[4]={1,-1,0,0};
int n;
int su=0;//被淹没岛屿个数 
void dfs(int x,int y)
{
	vis[x][y]=1;
	queue<pair<int,int>>qu;
	qu.push({x,y});
	int su1=0,su2=0;//su1该岛屿像素块个数,su2该岛屿内与海洋相邻像素块个数 
	while(!qu.empty())
	{
		auto k=qu.front();//取对头 
		qu.pop();
		int p=k.first,q=k.second;
		su1++;
		int f=0;
		//判断四个方位像素块性质 
		for(int i=0;i<4;i++)
		{
			int X=p+xx[i];
			int Y=q+yy[i];
			if(X>=0&&X<n&&Y>=0&&Y<n)
			{
				if(c[X][Y]=='.') f=1;//与海洋相邻 
				else if(vis[X][Y]==0)//相邻的,未被访问过的岛屿像素块 
				{
					vis[X][Y]=1;
					qu.push({X,Y});
				}
			}
		}
		if(f) su2++;
	}
	if(su1==su2) su++;//岛屿会被淹没 
	return ;
}
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++)
	cin>>c[i][j];
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			if(c[i][j]=='#'&&vis[i][j]==0)
			{
				dfs(i,j);
			}
		}
	}
	cout<<su<<endl;
}

问题 C: 发工资 

题目描述

    假如每位老师的工资都知道,最少需要准备多少张人民币,才能在给每位老师发工资的时候都不用老师找零呢?
    这里假设老师的工资都是正整数,单位元,人民币一共有100元、50元、10元、5元、2元、1元6种。

样例输入 
3
1 2 3
0
样例输出 
4
思路分析 

因为要求人民币张数最少,所以尽可能发面值大的,工资对从大到小的面值依次向下取整再加和即可。

AC代码
#include<iostream>
#include<algorithm>
using namespace std;
int x[6]={100,50,10,5,2,1};//面值 
int main()
{
	int n;
	cin>>n;
	while(n!=0)//n==0标志输入结束 
	{
		int su=0;
		for(int j=0;j<n;j++)
		{
			int c;
			cin>>c;
			//对面值从大到小依次向下取整 
			for(int i=0;i<6;i++)
			{
				su+=c/x[i];
				c=c%x[i];
			}
		}
		cout<<su<<endl;
		cin>>n;
	}
}

问题 D: 看病要排队 

题目描述

看病要排队这个是地球人都知道的常识。

不过经过细心的0068的观察,他发现了医院里排队还是有讲究的。0068所去的医院有三个医生(汗,这么少)同时看病。而看病的人病情有轻重,所以不能根据简单的先来先服务的原则。所以医院对每种病情规定了10种不同的优先级。级别为10的优先权最高,级别为1的优先权最低。医生在看病时,则会在他的队伍里面选择一个优先权最高的人进行诊治。如果遇到两个优先权一样的病人的话,则选择最早来排队的病人。

现在就请你帮助医院模拟这个看病过程。

输入

输入数据包含多组测试,请处理到文件结束。

每组数据第一行有一个正整数N(0<N<2000)表示发生事件的数目。

接下来有N行分别表示发生的事件。

一共有两种事件:

1:"IN A B",表示有一个拥有优先级B的病人要求医生A诊治。(0<A<=3,0<B<=10)

2:"OUT A",表示医生A进行了一次诊治,诊治完毕后,病人出院。(0<A<=3)

输出

对于每个"OUT A"事件,请在一行里面输出被诊治人的编号ID。如果该事件时无病人需要诊治,则输出"EMPTY"。

诊治人的编号ID的定义为:在一组测试中,"IN A B"事件发生第K次时,进来的病人ID即为K。从1开始编号。

样例输入 复制
7
IN 1 1
IN 1 2
OUT 1
OUT 2
IN 2 1
OUT 2
OUT 1
2
IN 1 1
OUT 1
样例输出 复制
2
EMPTY
3
1
1
思路分析 

定义三个set容器,分别对应三个窗口,set排序规则为先按优先级降序排列,再按序号升序排列,每次某个窗口医生诊治时,诊治对应容器最开始的那个病人。

AC代码 
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
class popo
{
	public:popo(int X,int Y)
	{
		x=X;
		y=Y;
	}
	int x;
	int y;
};
//自定义排序,先按优先级从大到小排,再按序号从小到大排 
class cmp
{
	public:bool operator()(const popo &p,const popo &q)
	{
		if(p.x==q.x)
		return p.y<q.y;
		return p.x>q.x;
	}
};
int main()
{
	int n;
	while(cin>>n)
	{
		multiset<popo,cmp>se[5];//set容器,可以在放入元素时做到实时排序 
		string s;
		int a,b;
		int k=1;
		for(int i=0;i<n;i++)
		{
			cin>>s;
			if(s=="IN")
			{
				cin>>a>>b;
				se[a].insert({b,k});//a窗口放入一个优先级为b,序号为k的病人 
				k++;
			}
			else
			{
				cin>>a;
				if(se[a].empty())//该窗口没有病人 
				cout<<"EMPTY"<<endl;
				else
				{
					cout<<se[a].begin()->y<<endl;//输出病人序号 
					se[a].erase(se[a].begin());//移除病人 
				}
			}
		}
	}
}

问题 E: 6.3.2.1 电话网络 

题目描述

电话公司正在创建一个新的电话网络,每个地方都有一个电话交换机(编号为1~N)。线路是双向的,并且总是将两个地方连接在一起,在灭个地方,线路都终止与电话交换机。从每个地方都可以通过线路达到其他地方,但不需要直接相连,可以进行多次交换。有时候在某个地方发生故障,会导致交换机无法运行。在这种情况下,除了无法到达失败的地方,还可能导致其他地方无法连接。这个地方(发生故障的地方)是至关重要的。请写程序来查找所有关键位置的数量。

样例输入 
5
5 1 2 3 4
0
6
2 1 3
5 4 6 2
0
0
样例输出 
1
2
思路分析 

额……这道题写着好心虚……说实话,我没太看懂题……但糊里糊涂就AC了。我的理解是对于每组样例的直达关系,如果某个点可以以他为起点直达其他一个或多个点,也就是说画成树后的非叶子节点,那么他就是关键节点。(还是去看其他同学的题解吧,总感觉我的理解不太对)

AC代码 
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
int main()
{
	int n;
	while(cin>>n&&n)
	{
		int su=0;
        int x;
		while(cin>>x&&x!=0)
        {
            int p=0;
            while(getchar()!='\n')
            {
                int y;
                cin>>y;
                p++;
            }
            if(p) su++;//有以该节点为起点的可以直达的其他节点 
        }
		cout<<su<<endl;
		cin>>n;
	}
}

问题 F: 7.1.4.1 重型运输

题目描述

你会得到城市的平面图,由十字路口之间的街道(有重量限制)描述,编号从1到n。你的任务是找到从1 (Hugo的位置)到n(客户的位置)可以运输的最大重量。你可以假设至少有一条路径。所有的街道都可以双向通行。

样例输入 
1
3 3
1 2 3
1 3 4
2 3 5
样例输出 
Scenario #1:
4
思路分析 

运用Dijkstra算法,求每个节点到源点的所有路径中最小值的最大值。每次以与源点距离最大值更新其他未确定结果的节点。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#define int long long
using namespace std;
int a[1005][1005];//存边 
int dis[1005];//到第i个点的最大承受重量 
int vis[1005];//该点结果是否已经确定 
signed main()
{
    int t;
    cin>>t;
    for(int o=1;o<=t;o++)
    {
        int n,m;
        cin>>n>>m;
        memset(a,0,sizeof a);
        memset(dis,0,sizeof dis);
        memset(vis,0,sizeof vis);
        while(m--)
        {
            int u,v,w;
            cin>>u>>v>>w;
            a[u][v]=max(a[u][v],w);
            a[v][u]=max(a[v][u],w);
        }
        //假设以1为源点 
        for(int i=1;i<=n;i++)
        dis[i]=a[1][i];
        int su=1;//su-已经确定的点的个数
		int mi;//mi-每次循环没确定的点中的最大值 
		int d;//记录最大值对应的位置 
        vis[1]=1;
        while(su<n)
        {
            mi=-1,d=-1;
            for(int i=1;i<=n;i++)
            {
                if(vis[i]==0&&mi<dis[i])
                {
                    mi=dis[i];
                    d=i;
                }
            }
            su++;
            vis[d]=1;
            for(int i=1;i<=n;i++)
            {
                if(vis[i]==0)
                {
                    dis[i]=max(dis[i],min(dis[d],a[d][i]));//更新其余点的结果,所有路径中每条路径的最小值的最大值
                    //dis[d]-d与源点可允许通行的最大货物量,a[d][i]-d与i之间可允许通行的最大货物量
                }
            }
        }
        cout<<"Scenario #"<<o<<":"<<endl;
        cout<<dis[n]<<endl;
        cout<<endl;
    }
}

问题 G: 打怪兽version-3 

题目描述

有N只怪兽的血量为Hi
你现在有2个技能
技能1:选择一个怪兽i,使其血量降低1点
技能2:选择一个怪兽i,使其血量变为0
问你需要使用多少个1技能可以杀死所有怪兽(其血量小于等于0即为死亡)
特别的,技能2只能使用K次

样例输入 
3 1
4 1 5
样例输出 
5
思路分析 

前min(n,k)个血量大的怪兽用技能2消灭,其余的用技能1消灭,即使用技能1的数量为其余怪兽血量总和。注意k可能会大于n。

#include<iostream>
#include<algorithm>
#include<set>
#define int long long
using namespace std;
int a[200005];
signed main()
{
	int n,k;
	cin>>n>>k;
	for(int i=0;i<n;i++)
	cin>>a[i];
	sort(a,a+n);
	int su=0;
	n-=min(n,k);
	for(int i=0;i<n;i++)
	su+=a[i];
	cout<<su<<endl;
}

问题 H: 2.4.1 间谍 

题目描述

X 国的情报委员受到一份可靠的消息,信息表明 Y 国将派间谍去窃取 X 国的机密文件。X 国指挥官手中有两份名单列表,一份是 Y 国派往 X 国的间谍名单列表,另一份是 X 国以前派往 Y 国的间谍名单。这两份名单列表可能有些重叠。因为间谍可能同时扮演两个角色,称之“双重间谍”。因此,Y 国可以把双重间谍送回 X 国。很明显,这对 X 国是有利的,因为双重间谍可以把 Y 国的机密文件带回,而不必担心在 Y 国边境被拘留。所以指挥官决定抓住由 Y 国派出的间谍,让普通人和双重间谍进入。那么你能确定指挥官需要抓捕的间谍名单吗?

样例输入 
8 4 3
Zhao  Qian Sun Li Zhou Wu Zheng Wang
Zhao Qian Sun Li
Zhao Zhou Zheng
2 2 2
Zhao Qian
Zhao Qian
Zhao Qian
样例输出 
Qian Sun Li
No enemy spy
思路分析 

暴力寻找在名单A,B中但不在名单C中的人。

AC代码
#include<iostream>
#include<algorithm>
#include<set>
#define int long long
using namespace std;
signed main()
{
	int a,b,c;
	string A[505],B[505],C[505];
	while(cin>>a>>b>>c)
	{
		int su=0;
		for(int i=0;i<a;i++)
		cin>>A[i];
		for(int i=0;i<b;i++)
		cin>>B[i];
		for(int i=0;i<c;i++)
		cin>>C[i];
		for(int i=0;i<a;i++)
		{
			for(int j=0;j<b;j++)
			{
				if(A[i]==B[j])
				{
					int f=0;
					for(int k=0;k<c;k++)
					{
						if(A[i]==C[k])
						{
							f=1;
							break;
						}
					}
					if(f==0)
					{
						su++;
						cout<<A[i]<<" ";
					}
					
				}
			}
		}
		if(su==0)
		cout<<"No enemy spy";
		cout<<endl;
	}
}

问题 I: 2.4.6 黑盒子 

题目描述

Black Box 是一种原始的数据库。它可以储存一个整数数组,还有一个特别的变量 ii。最开始的时候 Black Box 是空的.而 i=0i=0。这个 Black Box 要处理一串命令。

命令只有两种:

  • ADD(x):把 xx 元素放进 Black Box;
  • GET:ii 加 11,然后输出 Black Box 中第 ii 小的数。

记住:第 ii 小的数,就是 Black Box 里的数的按从小到大的顺序排序后的第 ii 个元素。

我们来演示一下一个有11个命令的命令串。(如下表所示)

序号操作ii数据库输出
1ADD(3)0033/
2GET113333
3ADD(1)111,31,3/
4GET221,31,333
5ADD(-4)22-4,1,3−4,1,3/
6ADD(2)22-4,1,2,3−4,1,2,3/
7ADD(8)22-4,1,2,3,8−4,1,2,3,8/
8ADD(-1000)22-1000,-4,1,2,3,8−1000,−4,1,2,3,8/
9GET33-1000,-4,1,2,3,8−1000,−4,1,2,3,811
10GET44-1000,-4,1,2,3,8−1000,−4,1,2,3,822
11ADD(2)44-1000,-4,1,2,2,3,8−1000,−4,1,2,2,3,8/

现在要求找出对于给定的命令串的最好的处理方法。ADD 命令共有 mm 个,GET 命令共有 nn 个。现在用两个整数数组来表示命令串:

  1. a1,a2,⋯,am:一串将要被放进 Black Box 的元素。例如上面的例子中 a=[3,1,−4,2,8,−1000,2]。
  2. u1,u2,⋯,un:表示第 u_iui 个元素被放进了 Black Box 里后就出现一个 GET 命令。例如上面的例子中 u=[1,2,6,6] 。输入数据不用判错。
样例输入 
7 4
3 1 -4 2 8 -1000 2
1 2 6 6
样例输出 
3
3
1
2
思路分析

每次排序前ui个元素,输出第j个。

AC代码
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int a[200005],b[200005];
int main()
{
	int m,n;
	cin>>m>>n;
	for(int i=0;i<m;i++)
	cin>>a[i];
	int j=0;
	for(int i=0;i<n;i++)
	{
		int x;
		cin>>x;
		sort(a,a+x);
		cout<<a[j]<<endl;
		j++;
	}
}

问题 J: 2.4.7 集合运算 

题目描述

给定N个集合,第1个集合Si有Ci个元素(集合可以包含两个相同的元素)。
集合中的每个元素都用1~10000 的正数表示。
查询两个给定元素i和j是否同时属于至少一个集合。
换句话说,确定是否存在一个数字k(1≤k≤N),使得元素i和元素j都属于Sk。

样例输入 
3
3 1 2 3
3 1 2 5
1 10
4
1 3
1 5
3 5
1 10
样例输出 
Yes
Yes
No
No
思路分析 

用bitset实现,bt[x]中第i位表示第i个集合中是否有x,再通过取&判断两个数是否在某同一个集合中。

AC代码
#include<iostream>
#include<algorithm>
#include<bitset>
using namespace std;
bitset<1000>bt[10005],c;
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		int m;
		cin>>m;
		for(int j=0;j<m;j++)
		{
			int x;
			cin>>x;
			bt[x].set(i);
		}
	}
	int q;
	cin>>q;
	for(int i=0;i<q;i++)
	{
		int x,y;
		cin>>x>>y;
		c=bt[x]&bt[y];
		if(c.any()) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
}

问题 K: 打怪兽version-4 

题目描述

有1只怪兽的血量为H
你现在有一个技能
你可以选择一个怪兽,假设它当前生命值为x
对其造成伤害之后,该怪兽会分裂成2个生命值为⌊x/2⌋的怪兽
问你需要使用多少次技能可以杀死所有怪兽

样例输入 
2
样例输出 
3
思路分析 

若f[i]表示打败血量为i的怪兽需要使用的技能次数,则f[i]=f[i/2]*2+1,但是直接写的话会TLE,但其实就相当于每隔2的整数次幂为前一个整数次幂的2倍加1

AC代码
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
signed main()
{
	int n;
	cin>>n;
	int su=0;
	int i=0;
	int p=1;
	while(i<n)
	{
		su=su*2+1;
		i+=p;
		p*=2;
	}
	cout<<su<<endl;
}

问题 L: 打怪兽version-5 

题目描述

有一只怪兽的血量为H
你现在有N个技能
技能i可以对怪兽造成Ai点伤害,但是需要Bi点魔力值
问你最少需要多少点魔力值,可以杀死该怪兽(其血量小于等于0即为死亡)
每个技能可以重复使用

样例输入 
9 3
8 3
4 2
2 1
样例输出 
4
思路分析 

好累呀,真的不想再打怪兽了,就不能和平共处吗……

类似于完全背包问题,不过传统背包问题是想让价值最大,本题是想让耗费魔力值最少。用f[i]表示消灭血量i需要的最少魔力值。所以赋初值时f[0]=0,其余皆为无穷大。若遍历到第j个技能,可得递推关系为,若i>=a[j],f[i]=min(f[i],f[i-a[j]]+b[j]),否则f[i]=min(f[i],f[0]+b[j])

AC代码
#include<iostream>
#include<algorithm>
#include<string.h>
#define int long
using namespace std;
int f[100005];//f[i]表示消灭血量i需要的最少魔力值
int n,h;
int a[10005],b[10005];
signed main()
{
	cin>>h>>n;
    for(int i=1;i<=n;i++)
    cin>>a[i]>>b[i];
    memset(f,0x3f,sizeof f);
    f[0]=0;
    for(int i=1;i<=h;i++)
    {
        for(int j=1;j<=n;j++)
        {
          if(i>=a[j])
          f[i]=min(f[i],f[i-a[j]]+b[j]);
          else
          {
              f[i]=min(f[i],f[0]+b[j]);
          }
          
        }
    }
    cout<<f[h]<<endl;
}

问题 M: 打怪兽version-6 

题目描述

有N只怪兽,每只怪兽都有一个坐标Xi,和其血量Hi
你有一个技能
每次技能你可以选择一个坐标x
对区间[x-D,x+D]的所有怪兽造成伤害A点
问你最少需要使用多少次技能可以杀死所有怪兽(其血量小于等于0即为死亡)

样例输入 
3 3 2
1 2
5 4
9 2
样例输出 
2
思路分析 

从左边开始消灭怪兽,则该点前面的怪兽都被消灭了,以该点为区间左端点消灭怪兽,若怪兽血量已经为0,则跳过。又由于整个区间搂到了1e9,不能直接用数组模拟数轴,所以需要先进行离散化处理。

怪兽终于要打完了哈哈哈哈哈

AC代码
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#define int long long
using namespace std;
vector<int>ve;
vector<pair<int,int>>v;
int f[1000005]={0};
int n,d,a;
//x在ve中的位置
int get(int x)
{
    return lower_bound(ve.begin(),ve.end(),x)-ve.begin()+1;
}
//离散化
int lo(int x)
{
    return (x&(-x));
}
//前缀和
int sum(int x)
{
    int su=0;
    for(int i=x;i;i-=lo(i))
    su+=f[i];
    return su;
}
void add(int x,int y)
{
    for(int i=x;i<=1000000;i+=lo(i))
    {
        f[i]+=y;
    }
}
signed main()
{
	cin>>n>>d>>a;
    int su=0;
    d=2*d+1;
    for(int i=0;i<n;i++)
    {
        int x,h;
        cin>>x>>h;
        ve.push_back(x);
        ve.push_back(x+d);
        v.push_back({x,h});
    }
    sort(v.begin(),v.end());
    sort(ve.begin(),ve.end());
    ve.erase(unique(ve.begin(),ve.end()),ve.end());//去重
    for(auto k:v)
    {
        int p,q;
        p=k.first;q=k.second;
        q=q+sum(get(p));
        if(q<=0) continue;
        int t=(q+a-1)/a;
        su+=t;
        add(get(p),-t*a);
        add(get(p+d),t*a);
    }
    cout<<su<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值