Codeforces Round #605 (Div. 3)
A: Three Friends
题意:
给出三个点的坐标,每一个点可以进行左移或者右移单位1,或者不移动,问最后三个点之间的相对距离最小值是多少。
解题思路:
因为每一个点最多三种情况,所以枚举一下即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m,k,t;
ll a,b,c,d;
ll num[3]={0,1,-1};
void solve(){
ll ans=((ll)1<<40);
cin>>a>>b>>c;
for(int i=0;i<3;i++){
ll a1=a+num[i];
for(int j=0;j<3;j++){
ll b1=b+num[j];
for(int p=0;p<3;p++){
ll c1=c+num[p];
ans=min(ans,abs(a1-b1)+abs(a1-c1)+abs(c1-b1));
}
}
}
cout<<ans<<endl;
}
int main(){
cin>>t;
while(t--){
solve();
}
return 0;
}
B:Snow Walking Robot
题意:
给出一段机器指令,你可以删去一些指令,使最后剩余的指令操作之后回到原点,且这个过程中不能经过相同点,问最多可以剩下多少指令。
解题思路:
分析可知,要回到原点,所以向上的操作次数等于向下的操作次数,向左的操作次数等于向右的操作次数,这个地方可以统计取min,对于可行的操作方案,只需要用剩余的操作按照左上右下画一个正方形即可。还需要注意一点,当向左向右的操作次数为0时,向上和向下只能为1,反之亦然。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m,k,t;
int a,b,c,d,e,f;
string s;
void solve(){
a=b=c=d=0;
cin>>s;
int len=s.size();
for(int i=0;i<len;i++){
if(s[i]=='L')a++;
else if(s[i]=='R')b++;
else if(s[i]=='U')c++;
else if(s[i]=='D')d++;
}
e=min(a,b);
f=min(c,d);
if((e==0&&f>1)||(e>1&&f==0)){e=min(1,e);f=min(1,f);}
cout<<(e+f)*2<<endl;
for(int i=0;i<e;i++)cout<<"L";
for(int i=0;i<f;i++)cout<<"U";
for(int i=0;i<e;i++)cout<<"R";
for(int i=0;i<f;i++)cout<<"D";
cout<<endl;
}
int main(){
cin>>t;
while(t--){
solve();
}
return 0;
}
C:Yet Another Broken Keyboard
题意:
给出一段字符串,其中一些字符你打不出来,为其他字符你可以打出来的子串个数最多为多少。
解题思路:
可以统计连续的可以打出的字符的长度,假设为n那么此段区间的答案贡献是n*(n+1)/2,累加一下即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int n,m,k,t;
int vis[27];
char c;
string s;
void solve(){
cin>>n>>m;
cin>>s;
ll ans=0;
for(int i=1;i<=m;i++)cin>>c,vis[c-96]=1;
vector<int>v;
v.push_back(-1);
for(int i=0;i<n;i++)if(!vis[s[i]-96])v.push_back(i);
v.push_back(n);
for(int i=1;i<v.size();i++){
ans+=(ll)(v[i]-v[i-1]-1)*(v[i]-v[i-1])/2;//相邻差就是可以打出的字母区间长度
}
cout<<ans<<endl;
}
int main(){
solve();
return 0;
}
D:Remove One Element
题意:
给出一段正整数序列,你可以选择移除一个,问序列中最长严格递增子段长度是多少。
解题思路:
可以用dp方程进行转移,dp[i][0]代表以i为结尾不采用移除的最长子段,dp[i][1]代表采用移除可以达到的最大字段。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int n,m,k,t;
int a[N],dp[N][2];
void solve(){
int ans=1;
dp[1][0]=1,dp[1][1]=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=2;i<=n;i++){
if(a[i]>a[i-1]){
dp[i][0]=dp[i-1][0]+1;
if(a[i]>a[i-2]&&i>2)dp[i][1]=max(dp[i-1][1],dp[i-2][0])+1;//如果大于前前一项则可以采用移除前一项和不移动取max
else dp[i][1]=dp[i-1][1]+1;
}else {
if(a[i]>a[i-2]&&i>2)dp[i][1]=dp[i-2][0]+1,dp[i][0]=1;
else dp[i][0]=1,dp[i][1]=1;
}
ans=max(ans,max(dp[i][0],dp[i][1]));
}
printf("%d\n",ans);
}
int main(){
solve();
return 0;
}
E:Nearest Opposite Parity
题意:
给出一段整数序列,你可以在第i个位置向左或者向右移动a[i]个单元格,如果不越界,问在第i个位置移动到与它奇偶性不同的单元格的最小步数为多少。
解题思路:
这道题理解的还不是很理解,解题过程是先把能到达的两个点反向建一条边,如果这两个点奇偶性不同,则将当前点加入到队列中,再用这个点来更新奇偶性相同的点。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int n,m,k,t;
int a[N],ans[N],vis[N];
vector<int>v[N];
queue<int>q;
void solve(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),ans[i]=1e9;
for(int i=1;i<=n;i++){
int now=i-a[i];
if(now>=1){
v[now].push_back(i);//反向健边
if(a[now]%2!=a[i]%2){
ans[i]=1;
if(!vis[i])q.push(i);//将一步就可以到达且奇偶性不同的加到队列中
vis[i]=1;
}
}
now=i+a[i];
if(now<=n){
v[now].push_back(i);
if(a[now]%2!=a[i]%2){
ans[i]=1;
if(!vis[i])q.push(i);
vis[i]=1;
}
}
}
while(!q.empty()){
int now=q.front();
q.pop();
for(int nxt:v[now]){//更新可以到达点的最短距离
if(!vis[nxt]&&a[nxt]%2==a[now]%2&&ans[now]+1<ans[nxt]){
vis[nxt]=1;
ans[nxt]=ans[now]+1;
q.push(nxt);
}
}
}
for(int i=1;i<=n;i++){
if(ans[i]==1e9)printf("-1 ");
else printf("%d ",ans[i]);
}
}
int main(){
solve();
return 0;
}