A. Primary Task
一道签到题,因为给的数据范围是1e5还算小,所以当做数字读取,分类讨论也是可以做出来的。当然,更加方便的还是利用stl里的string类,可以使用 substr 来取子段分析。
B. Seating in a Bus
一道简单的模拟题,只需要开一个2e5的vis数据来确定那些位置上已经有人了就可以模拟出来。注意:数组要适当开大一点,第一个位置要空出,以免在访问时出现数组越界的情况。
C. Numeric String Template
也是一道模拟题,解题逻辑在题目中已经提及:
首先,利用长度做一个快速的判断。
其次,相同数字对应的字符要一样;相同字符对应的数字要一样。
1 2 3 1 4
a b e a e
其中,1对应的字符都为a;但e对应的数字不同,所以该样例答案是NO
主要考察的点还是对于stl中map的基本运用。
map <int,char> mp1;
map <int,char> mp2;
mp1.find(a[i])==mp1.end() //判断是否存在该key
mp1[a[i]]=s[i]; //不存在赋值,存在就比较
D. Right Left Wrong
题目描述
有一个数组a,其中每个数字对应一个"L"或者对应”R“,定义为字符串s。每次操作找到一组LR(s[le]=="L";s[ri]=='R';le<ri)将区间内的所有数字求和,然后这个区间所有的对应字符更改为’,‘ 要求求出最大的和是多少。
样例
6//有几个元素 答案
3 5 1 4 3 2
L R L L L R 18
2
2 8
L R 10
2
3 9
R L 0
5
1 2 3 4 5
L R L R R 22=(3+4)+(1+2+3+4+5)
思路
这道题有两个思路。
思路一:
因为要求最值,第一时间就想到了DP和贪心。首先考虑到的是,如果只能选择一对LR对,如何选择可以使得sum最大。简单思考可以得到答案,选择最左边的L和最右边的R。
这时候注意到,最外面一对LR对,对区间里面的字符是没有要求的,也就是说,区间内的元素中如果能够再找到一对LR对,就是在原来答案上加了一份(这也是为什么最后一个样例的答案是22而不是15)。那么这里就可以理解为,每次找到最外面一个LR,然后把外面的部分不要了,在里面继续去找一对LR(也就是最优子结构)。那么这种情况就明显要使用到双指针,框定一个区间范围(l=0,r=n-1)。
由于每次要对一个区间里的元素求和,所以要利用到前缀和快速求区间和,不然会超时。
思路二:(官方解法)
由于一个数会出现多次,考虑这个数的权重(即出现次数)。既然要出现,必然是出于一个LR片段的中间,所以转换到 i-th 个数被多少个LR对包住。
比对 i 号位置左边L个数,右边R个数,即可得到答案(第i号位置也要考虑)。LR对个数为min(cntL,cntR);
收获
求最值的时候考虑到贪心和dp。这道题正向逻辑其实是,找到最里面的LR对,sum后变成’,‘然后每次向外扩一个LR对。但在实际实现的时候是从最外面开始找。
如果从思路二的角度来说,由于一个元素可能被计算到多次,那么能否找到每个元素被计算到的次数,也就是找到每个数的权值。
ac代码(思路一)
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
long long b[N];
int main()
{
int t,n,a[N];
char s[N];
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>s[i];
}
b[0]=0;
for(int i=1;i<=n;i++){
b[i]=b[i-1]+a[i];
}
long long ans=0;
int le=1,ri=n;
while(le<ri){
while(le<=n && s[le]!='L'){
le++;
}
while(ri>0 && s[ri]!='R'){
ri--;
}
if(le<ri && le<=n && ri>0){
ans=ans+b[ri]-b[le-1];
}
le++;
ri--;
}
cout<<ans<<endl;
}
}
E. Photoshoot for Gorillas
题目描述
有一个n*m的矩形,k*k的范围。(可以放到矩形里面)矩形内每一个点都有分数,分数的值为所有能放进矩形中的k*k正方形形里面,覆盖到这个点的个数。
另外有一个数组a,里面有w个正整数;要把这些数字放进矩形,每次操作会得到一个分数,分数值=ai*矩形分数。求最终得到的最大得分是多少
思路
跟D题的思路二有点相似。主要难点在于求得矩阵中每个点的分数,即有多少个k*k可以包含在里面。
在做到二维的题目时,最首先就是分成x,y轴分开讨论再中和。当分开讨论的时候,这道题的二维就转换成了两个一维问题,一段长度为n的线段中,x点上最多有多少个长度为k的线段。
那么有,最左边线段开头le=max(0,i-k+1) 最右边ri=min(i,n-k); 个数=(ri-le+1)
那么同理y轴方向,那么最后要怎么综合呢? 简单思考就能得到,两者应该相乘。
收获
在做到二维问题的时候,首先考虑转换成两个一维问题解决,这在很多题目中都很适用。
ac代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
long long ans[N];
bool cmp(int a,int b){
return a>b;
}
int main()
{
int t,n,m,k,w;
int a[N];
cin>>t;
while(t--){
cin>>n>>m>>k;
cin>>w;
for(int i=0;i<w;i++){
cin>>a[i];
}
int cnt=0;
for(int i=0;i<n;i++){
for(int j = 0;j<m;j++){
int lex=max(0,i-k+1);
int rix=min(i,n-k);
int ley=max(0,j-k+1);
int riy=min(j,m-k);
ans[cnt]=(rix-lex+1)*(riy-ley+1);
//cout<<ans[cnt]<<" ";
cnt++;
}
}
long long res=0;
int i=0;
sort(a,a+w,cmp);
sort(ans,ans+cnt,cmp);
while(w>0 && cnt>=0){
//cout<<a[i]<<" "<<ans[i]<<endl;
res+=a[i]*ans[i];
i++;
w--;cnt--;
}
cout<<res<<endl;
}
}