目录
1.选数
2.奇怪的电梯
3.无线通讯网
4. Rotate Colored Subsequence
5.LOWER
6.Error Correction
1.选数
P1036 [NOIP2002 普及组] 选数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
已知 n 个整数 1,2,⋯ ,x1,x2,⋯,xn,以及 1 个整数 k(k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:
3+7+12=22
3+7+19=29
7+12+19=38
3+12+19=34
现在,要求你计算出和为素数共有多少种。
例如上例,只有一种的和为素数:3+7+19=29。
输入
第一行两个空格隔开的整数n,k(1≤n≤20,k<n)。
第二行 n 个整数,分别为 1,2,⋯ ,x1,x2,⋯,xn(1≤xi≤5×10^6)。
输出
输出一个整数,表示种类数。
Sample 1
Inputcopy | Outputcopy |
---|---|
4 3 3 7 12 19 | 1 |
一道基础的深搜题,从数组的第一个数开始深搜,,当搜到的数字个数达到k个,那么计算此时数的和是否为素数。
下面是完整AC代码:
#include<bits/stdc++.h>
using namespace std;
int a[30],ans,n,k;
int isprime(int n)
{
if(n<=1) return 0;//用于找素数
for(int i=2;i*i<=n;i++){
if(n%i==0) return 0;
}
return 1;
}
void dfs(int m,int sum,int x)
{
if(m==k)
{
if(isprime(sum))//如果此时和为素数,答案总数加一
ans++;
return;//返回,不用继续搜
}
for(int i=x;i<n;i++){
dfs(m+1,sum+a[i],i+1);
}
}
int main()
{
cin>>n>>k;
for(int i=0;i<n;i++) cin>>a[i];
dfs(0,0,0);
cout<<ans<<endl;
return 0;
}
因为n最大为20,也可以暴力for循环,但是要将近900行代码了,危险系数极高,不建议使用。
2.奇怪的电梯
P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i 层楼(1≤i≤N)上有一个数字 Ki(0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例:3,3,1,2,5 代表了 Ki(K1=3,K2=3,……),从 1 楼开始。在 1 楼,按“上”可以到 4 楼,按“下”是不起作用的,因为没有 −2 楼。那么,从 A 楼到 B 楼至少要按几次按钮呢?
输入
共二行。
第一行为三个用空格隔开的正整数,表示 N,A,B(1≤N≤200,1≤A,B≤N)。
第二行为 N 个用空格隔开的非负整数,表示 Ki。
输出
一行,即最少按键次数,若无法到达,则输出 -1
。
Sample 1
Inputcopy | Outputcopy |
---|---|
5 1 5 3 3 1 2 5 | 3 |
这也是一道搜索题,当你在起始位置时,可以选择上,也可以选择下,如果会到达电梯不能到达的楼层那就不能到达,我们这里使用bfs解决。
下面是完整AC代码:
#include<bits/stdc++.h>
using namespace std;
struct node{
int x,step;
}start;
int n,s,e,a[210],v[210];
queue<node>q;
int main()
{
cin>>n>>s>>e;
for(int i=1;i<=n;i++) cin>>a[i];
start.x=s,start.step=0;
q.push(start);
while(!q.empty())
{
node k=q.front();
q.pop();
if(k.x==e)
{
cout<<k.step<<endl;//到达目标位置,直接输出步数
return 0;
}
for(int i=-1;i<=1;i+=2){//向上走或者向下
int tx=k.x+i*a[k.x];
if(tx>n||tx<1||v[tx]) continue;//走过或者走到不能到达的楼层,跳过
v[tx]=1;
q.push({tx,k.step+1});
}
}
cout<<-1<<endl;//不能到达,输出-1
return 0;
}
3.无线通讯网
P1991 无线通讯网 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
国防部计划用无线网络连接若干个边防哨所。2 种不同的通讯技术用来搭建无线网络;
每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。
任意两个配备了一条卫星电话线路的哨所(两边都有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过 D,这是受收发器的功率限制。收发器的功率越高,通话距离 D 会更远,但同时价格也会更贵。
收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个 D。你的任务是确定收发器必须的最小通话距离 D,使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。
输入
第一行,22 个整数 S 和 P,S 表示可安装的卫星电话的哨所数,P 表示边防哨所的数量。
接下里 P 行,每行两个整数 x,y 描述一个哨所的平面坐标 (x,y),以 km 为单位。
输出
第一行,1 个实数 D,表示无线电收发器的最小传输距离,精确到小数点后两位。
Sample 1
Inputcopy | Outputcopy |
---|---|
2 4 0 100 0 300 0 600 150 750 | 212.13 |
这道题我一开始没有看懂,到后面经过高人指导才了解,我们需要输出最小的传输距离的前提是这些距离可以联通覆盖所有放哨点,卫星电话就是不管距离多远也可以传输,就不用无线电收发器。
还是使用Kruskal算法,我感觉这道题的边排序与Kruskal算法蛮适合。
看数据范围,P表示放哨所的数量,那我们的边最多是P*P,所以给代表边的结构体数组开范围需要开大一些。
边的长度可以利用数学知识(勾股定理)来求。
double dist(double x1,double y1,double x2,double y2)
{
return sqrt(pow(x1-x2,2)+pow(y1-y2,2));
}
其他的就是存边操作。
for(int i=1;i<=m;i++){
for(int j=i+1;j<=m;j++){
a[++re].x=i;
a[re].y=j;
a[re].w=dist(x[i],y[i],x[j],y[j]);
}
}
下面是完整AC代码:
#include<bits/stdc++.h>
using namespace std;
int pre[600];
struct node{
int x,y;
double w;
}a[300000];
int x[600],y[600],re;
double ans[300000];
double dist(double x1,double y1,double x2,double y2)
{
return sqrt(pow(x1-x2,2)+pow(y1-y2,2));
}
int Find(int x)
{
if(pre[x]==x) return x;
return pre[x]=Find(pre[x]);
}
bool cmp(node &k1,node &k2)
{
return k1.w<k2.w;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>x[i]>>y[i];
pre[i]=i;
}
for(int i=1;i<=m;i++){
for(int j=i+1;j<=m;j++){
a[++re].x=i;
a[re].y=j;//存边
a[re].w=dist(x[i],y[i],x[j],y[j]);
}
}
sort(a+1,a+1+re,cmp);
int cnt=0;
for(int i=1;i<=re;i++){
int fx=Find(a[i].x);
int fy=Find(a[i].y);
if(fx==fy) continue;
pre[fx]=fy;
cnt++;
if(cnt==m-n)//减去n是因为多出的n可以使用卫星电话
{
printf("%.2lf\n",a[i].w);
return 0;
}
}
return 0;
}
4. Rotate Colored Subsequence
C - 旋转彩色子序列 (atcoder.jp)
问题描述
给定一个长度为N的字符串S,由小写英文字母组成。 S的每个字符都被涂上了M种颜色中的一种:颜色1,颜色2,...,颜色M;对于每个i=1,2,…,N,S的第i个字符被涂上了颜色Ci。
按照给定的顺序,对每个i=1,2,…,M执行以下操作。
- 对颜色为i的部分进行1次右循环移位。 也就是说,如果从左到右第p1、p2、p3、……、pk个字符被涂上了颜色i,那么同时用S的第pk、p1、p2、……、pk−1个字符替换S的第p1、p2、p3、……、pk个字符。
打印上述操作后的最终S。
约束条件保证S中至少有一个字符被涂上了每一种M。
约束条件
- 1≤M≤N≤2×10^5
- 1≤Ci≤M
- N、M和Ci都是整数。
- S是一个长度为N的由小写英文字母组成的字符串。
- 对于每个整数1≤i≤M,存在一个整数1≤j≤N使得Cj=i。
输入
输入以以下格式从标准输入中给出:
N M S C1 C2 …… CN
输出
打印答案。
示例 1
Inputcopy | Outputcopy |
---|---|
8 3 apzbqrcs 1 2 3 1 2 2 1 2 | cszapqbr |
示例 2
Inputcopy | Outputcopy |
---|---|
2 1 aa 1 1 | aa |
题目要求我们对具有相同颜色(具有相同数字)的字母执行操作:
前面的字母替换后面相同颜色的字母,最后一个相同颜色的字母替换第一个相同颜色的字母。
所以我们可以使用队列,对每个不同颜色的字母入队,用一个数字标记每个颜色的第一个字母的位置。
下面是完整AC代码:
#include<bits/stdc++.h>
using namespace std;
int n, m;
queue<char>q[200010];
int k[200010];
char s[200010];
int main()
{
cin >> n >> m;
for(int i=1;i<=n;i++) cin>>s[i];
for (int i = 1; i <= n; i++) {
int x; cin >> x;
if(q[x].empty())//此时记录第一个颜色的字母位置,并且将其入队
{
q[x].push(s[i]);
k[x]=i;
}
else
{
q[x].push(s[i]);//颜色替换,将次颜色的新的字母再从入队
s[i]=q[x].front();
q[x].pop();
}
}
for(int i=1;i<=m;i++){
if(k[i])//将第一个相同颜色字母替换成最后一个
s[k[i]]=q[i].front();
}
for(int i=1;i<=n;i++){
cout<<s[i];//输出
}
return 0;
}
5.LOWER
D - 下 (atcoder.jp)
题目描述
给定一个长度为 N 的字符串 S,由大写和小写英文字母组成。
让我们对字符串 S 执行 Q 次操作。 第 i 次操作(1≤i≤Q) 由一个包含两个整数和一个字符的元组 (ti,xi,ci) 表示,具体如下。
- 如果 ti=1,将 S 的第 xi 个字符改为 ci。
- 如果 ti=2,将 S 中所有大写字母转换为小写(不要使用xi,ci 进行此操作)。
- 如果 ti=3,将 S 中所有小写字母转换为大写(不要使用 xi,ci 进行此操作)。
在执行完 Q 次操作后,输出 S。
约束条件
- 1≤N≤5×10^5
- S 是一个长度为 N 的字符串,由大写和小写英文字母组成。
- 1≤Q≤5×10^5
- 1≤ti≤3 (1≤i≤Q)
- 如果 ti=1,那么 1≤xi≤N (1≤i≤Q)。
- ci 是一个大写字母或小写字母。
- 如果 ti≠1,那么 xi=0 并且 ci=
'a'
。 - N,Q,ti,xi 都是整数。
输入
输入以以下格式从标准输入给出:
N S Q t1 x1 c1 t2 x2 c2 ⋮⋮ tQ xQ cQ
输出
在一行中输出答案。
示例 1
Inputcopy | Outputcopy |
---|---|
7 AtCoder 5 1 4 i 3 0 a 1 5 b 2 0 a 1 4 Y | atcYber |
示例 2
Inputcopy | Outputcopy |
---|---|
35 TheQuickBrownFoxJumpsOverTheLazyDog 10 2 0 a 1 19 G 1 13 m 1 2 E 1 21 F 2 0 a 1 27 b 3 0 a 3 0 a 1 15 i | TEEQUICKBROWMFiXJUGPFOVERTBELAZYDOG |
每次输入,t=1时,直接先对数组修改,题目会出现下面三种情况:
1.最后输入的t=2,那么直接将所有的大写字母变成小写字母,然后输出。
2.最后输入的t=3,那么直接将所有的小写字母变成大写字母,然后输出。
3.最后有若干个t=1出现比最后一个t=2或者t=3后出现,用数组记录此时出现的字母、下标和时间,修改后输出。
下面是完整AC代码:
#include<bits/stdc++.h>
using namespace std;
string a;
long long l,r,z,k,v[500010][2],m=0;
char f[500010];
int main()
{
long long n;
cin>>n>>a;
long long q;
cin>>q;
for(int i=1;i<=q;i++){
int t,x;
char y;
cin>>t>>x>>y;
if(t==1)
{
a[x-1]=y;//直接修改
v[++m][0]=i;//记录时间
v[m][1]=x;//记录位置
f[m]=y;//记录字母
}
if(t==2)
l=i;
if(t==3)
r=i;
}
if(l>r)
{
for(int i=0;i<n;i++){
if(a[i]>='A'&&a[i]<='Z')
a[i]+=32;
}
for(int i=1;i<=m;i++){
if(l<v[i][0])//按照出现时间修改
{
a[v[i][1]-1]=f[i];
}
}
}
else if(l<r)//同理
{
for(int i=0;i<n;i++){
if(a[i]>='a'&&a[i]<='z')
a[i]-=32;
}
for(int i=1;i<=m;i++){
if(r<v[i][0])
{
a[v[i][1]-1]=f[i];
}
}
}
//如果l==r,那么证明t没有等于2和3的情况,不用处理。
cout<<a<<endl;
return 0;
}
6.Error Correction
C - 纠错 (atcoder.jp)
题目描述
高桥给青木发送了一个由小写英文字母组成的字符串 T。结果,青木收到了一个由小写英文字母组成的字符串 ′T′。
′T′ 可能已经从 T 改变而来。具体来说,我们知道以下四种情况中的一种成立。
- ′T′ 等于 T。
- ′T′ 是在 T 中插入一个小写英文字母得到的。
- ′T′ 是在 T 中删除一个字符得到的。
- 'T′ 是将 T 中的一个字符改变成另一个小写英文字母得到的。
给定青木收到的字符串 ′T′ 和 N 个由小写英文字母组成的符串 S1,S2,…,SN。找出所有可能与高桥发送的字符串 T 相等的字符串。
限制条件
- N 是一个整数。
- 1≤N≤5×10^5
- Si 和 ′T′ 是长度在 1 到 5×10^5 之间的字符串,由小写英文字母组成。
- S1,S2,…,SN 的总长度最多为5×10^5。
输入
输入以以下格式从标准输入给出:
N ′T′ S1 S2 ⋮⋮ SN
输出
令 (i1,i2,…,iK) 为所有可能与 T 相等的字符串的索引序列,按升序排列。 以以下格式输出这个序列的长度 K 和序列本身:
K i1 i2 …… iK
示例 1
Inputcopy | Outputcopy |
---|---|
5 ababc ababc babc abacbc abdbc abbac | 4 1 2 3 4 |
示例 2
Inputcopy | Outputcopy |
---|---|
1 aoki takahashi | 0 |
示例 3
Inputcopy | Outputcopy |
---|---|
9 atcoder atoder atcode athqcoder atcoder tacoder jttcoder atoder atceoder atcoer | 6 1 2 4 7 8 9 |
按照要求,我们需要对每次输入的字符串判断,看看它是否有可能是原串改变过来的,条件成立需要满足下面要求:
1.和原串相同,没有改变。
2.在原串的基础上插入一个小写字母。
3.在原串的基础上删除一个小写字母。
4.在原串的基础上改变一个小写字母成另外一个小写字母。
所以我们可以写一个函数来专门判断条件是否成立。
int check(string s)
{
int ans=0;
if(abs((int)a.size()-(int)s.size())>1) return 0;
if(a.size()==s.size())
{
for(int i=0;i<a.size();i++){
if(a[i]!=s[i]) ans++;
}
}
else if(a.size()+1==s.size())
{
int i=0,j=0;
while(i<a.size()&&j<s.size()){
if(a[i]!=s[j]) j++,ans++;
else i++,j++;
}
}
else
{
int i=0,j=0;
while(i<a.size()&&j<s.size()){
if(a[i]!=s[j]) i++,ans++;
else i++,j++;
}
}
return ans<=1;
}
下面是完整AC代码:
#include<bits/stdc++.h>
using namespace std;
string a;
int n,re[500010],k;
int check(string s)
{
int ans=0;
if(abs((int)a.size()-(int)s.size())>1) return 0;//如果相差长度大于1,那么肯定不满足
if(a.size()==s.size())
{
for(int i=0;i<a.size();i++){
if(a[i]!=s[i]) ans++;//判断相异个数,控制在1以内则成立
}
}
else if(a.size()+1==s.size())
{
int i=0,j=0;
while(i<a.size()&&j<s.size()){
if(a[i]!=s[j]) j++,ans++;//比对字母,若是不同,只将长度长的一方+1
else i++,j++;
}
}
else
{
int i=0,j=0;
while(i<a.size()&&j<s.size()){//同理
if(a[i]!=s[j]) i++,ans++;
else i++,j++;
}
}
return ans<=1;
}
void solve(int i)
{
string s;cin>>s;
if(check(s))re[++k]=i;//记录序号
}
int main()
{
cin>>n>>a;
for(int i=1;i<=n;i++)
solve(i);
cout<<k<<endl;//输出
for(int i=1;i<=k;i++)
cout<<re[i]<<" ";
return 0;
}
其实还有一道第七题,但是不会。