2019年2月16日训练日记
今天写几个之前的题目:
奶糖在左,果糖在右
(队列排序查特殊值累加问题)
在 ACM 集训队里,LWY 学长和 JH 学姐是很好的朋友,从他们每天称呼对方“儿子”就能看出他们的
关系非常的亲近。
而集训队里有一个糖果盒,有奶糖和水果糖两种,在盒子里排成一排,LWY 学长很喜欢吃糖果,但是他的强迫症非常严重,他每次只拿两个相邻的糖果,而且必须是奶糖在左边,果糖在右边他才会拿,JH 学姐每次他拿完糖果之后,都会在拿糖果的原处放新的糖果进去,但是她不想让 LWY 学长吃,就会故意果糖放左边,奶糖放右边。
显而易见,LWY 学长没机会永远吃糖果了,他看了看糖果盒中糖果的排列,他想知道他最多还能吃多少颗糖果。
输入
第一行一个数字 N(1<=N<=200,60%数据;1<=N<=100‘000,100%数据);
第二行是一个长度为 N 的字符串,M 代表奶糖,F 代表水果糖。
输出
一行一个数字,输出 LWY 学长最多能吃几颗糖果,数据可能过大,可以用 long long int 来存储结果。
样例输入
5
MFMFM
样例输出
6
题解:
这道题关键点是看出糖果交换的规律,每次交换实质上是果糖移动到左边奶糖的左边,也就是对于每
一颗果糖,它需要移动的次数是它左边奶糖的个数,所以答案只需要统计每一颗果糖右边奶糖的个数,再加起来,就是交换次数,结果是交换次数乘以 2,因为每次交换吃两颗糖。
标程:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,m=0,i,res=0;
string s; //string 是字符串变量,char 数组亦可
cin>>n;
cin>>s;
for(i=0;i<n;i++)
{
if(s[i]=='M')
{
m++;//发现奶糖,统计个数
}
else
{
res+=m;//发现果糖,记录个数
}
}
res*=2;
cout<<res<<endl;
}
矩形相交小问题
(寻找规律求解有时候比正常做法要简便,不能局限于学过的东西而忘记了不一样的做法,多思考一下是否存在多种解题思路,比如此题中数组的使用是否真的简便)
这是一道简单的推理题。平面上有若干个点,每个点的坐标为(x,y)。
众所周知,给定一个矩形的左上角坐标(xl,yl)和右下角坐标(xr,yr)即可唯一的确定
一个矩形。
那么问题来了,给你两个矩形的左上角坐标和右下角坐标,求这两个矩形相交部
分的面积。注意,不相交则输出 0。
输入
两行,第一行为 4 个整数 a,b,c,d,分别表示第一个矩形的左上角坐标(a,b)和右下
角坐标(c,d)。
第二行为 4 个整数 e,f,g,h,分别表示第个矩形的左上角坐标(e,f)和右下角坐标
(g,h)。
0<=a,b,c,d,e,f,g,h<=100
输出
这两个矩形相交部分的面积。不相交则输出 0。
样例
input
1 1 5 5
2 2 3 3
output
1
例程:
这里我给出两种做法:
方法一:
在纸上画一下矩形相交时候的情况,发现两个矩形相交时,交矩形的左上角的坐标
就是这两个矩形的左上角坐标中的 x、y 分别取最大值,交矩形的右下角的坐标就
是这两个矩形的右下角坐标中的 x、y 分别取最小值,最后交矩形的长乘高就是答
案。此解法效率极高,时间复杂度仅为 O(1)。
代码:
#include<bits/stdc++.h>
using namespace std;
int a[8];
int main()
{
for(int i=0;i<8;i++)
scanf("%d",&a[i]);
a[0]=max(a[0],a[4]);
a[1]=max(a[1],a[5]);
a[2]=min(a[2],a[6]);
a[3]=min(a[3],a[7]);
if(a[0]>a[2]||a[1]>a[3]) puts("0");
else printf("%d\n",(a[2]-a[0])*(a[3]-a[1]));
return 0;
}
方法二:把矩形想象成一个左闭右开的二维区间(二维数组),(x,y)表示以点(x,y)
为左上角坐标的一个 11 的小矩形。对于给定的第一个矩形,将其每个小矩形都
加一,再对第二个矩形求有多少个被加一的 11 的小矩形在第一个矩形中即可。
此解法效率较低,时间复杂度为 O(n*n),且开二维数组更浪费内存空间,但是足
以满分通过本题。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
int a,b,c,d,e,f,g,h;
int aa[maxn][maxn];
int main()
{
scanf("%d%d%d%d%d%d%d%d",&a,&b,&c,&d,&e,&f,&g,&h);
for(int i=a;i<c;i++)
for(int j=b;j<d;j++)
aa[i][j]++;
a=0;
for(int i=e;i<g;i++)
for(int j=f;j<h;j++)
a+=aa[i][j];
printf("%d\n",a);
return 0;
}
指纹
(介绍了何为子序列:
子数列定义:某个数列的子数列是从最初数列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新数列。
例如:数列[1 2],[1 3 5]是数列[1 2 3 5]的子数列,但数列[2,1][1,4]不是。)
你被锁在一个房间里,门上有一个键盘,键盘上的 10 个键与 0 到 9 之间的数字相
对应,其中 m 个键上有指纹。
要逃出房间,你需要输入正确的密码。密码是一个数字序列。
你找到了一个长度为 n 的数字序列 a,您认为正确的密码,是数列 a 中最长的只包
含对应键上有指纹的数字的子数列,你能找到正确的密码,逃出房间吗?
子数列定义:某个数列的子数列是从最初数列通过去除某些元素但不破坏余下元素
的相对位置(在前或在后)而形成的新数列。
例如:数列[1 2],[1 3 5]是数列[1 2 3 5]的子数列,但数列[2,1][1,4]不是。
输入
第一行包含两个整数 n,m(0<=n<=1000,0<=m<=10)——表示数字序列 a 的位数,
以及键盘上有指纹的键的数量。
第二行包含 n 个数字,?1, ?2,⋯ ?? (0≤??≤9),数字之间以空格分开,表示你找到
的数字序列。
第三行包含 m 个数字,?1, ?2,⋯ ?? (0≤??≤9),数字之间以空格分开,表示有指纹
的键上的数字。
输出
在一行中打印密码,各位密码之间以空格分隔。如果结果数列是空的,那么什么也
不打印。
样例
Input1
7 3
3 5 7 1 6 2 8
1 2 7
Output1
7 1 2
Input2
4 4
3 4 1 0
0 1 7 9
Output2
1 0
例程:
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1005],ans,x,vis[20];
int main()
{
scanf("%d%d",&n,&m);
///记录数字序列 a
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
///对于有指纹的数,进行记录,vis 数组的对应值为 1。
for(int i=1;i<=m;i++)
{
scanf("%d",&x);
vis[x]=1;
}
///ans 统计有指纹的数字的个数,用于后面的空格回车判断,但本题,没有卡这一
步,最后多输出空格,没有换行,也能 AC
for(int i=1;i<=n;i++)
{
if(vis[a[i]]) ans++;
}
///输出答案,控制空格,与最后的回车
for(int i=1;i<=n;i++)
{
if(vis[a[i]])
{
printf("%d",a[i]);
ans--;
if(ans) printf(" ");
else printf("\n");
}
}
}