AtCoder Beginner Contest 339
文章目录
A - TLD
模拟
记录最后一个点的位置,直接substr即可
#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
typedef long long ll;
const int N = 1e9;
int main() {
string s;
cin >> s;
int pos = 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '.') {
pos = i;
}
}
s = s.substr(pos + 1);
cout << s << endl;
}
B - Langton’s Takahashi
模拟,数组
重要的是三个函数:
一个是顺时针函数,令坐标顺时针90方向走
一个是逆时针函数,令坐标逆时针90度方向走
一个是check函数,如果越界了,就更新坐标即可
#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
typedef long long ll;
const int N = 1000+7;
char a[N][N];
// 0 1 2 3 上下左右
int dx[]={-1,1,0,0}; //上下左右
int dy[]={0,0,-1,1};
int h,w,n;
void shun(int &face,int &x,int &y){ //顺时针朝向
if(face==0)face=3;
else if(face==1)face=2;
else if(face==2)face=0;
else if(face==3)face=1;
x+=dx[face];
y+=dy[face];
}
void ni(int &face,int &x,int &y){ //逆时针朝向
if(face==0)face=2;
else if(face==1)face=3;
else if(face==2)face=1;
else if(face==3)face=0;
x+=dx[face];
y+=dy[face];
}
void check(int &x,int &y){
if(x>=1 and x<=h and y>=1 and y<=w)return;
if(x<1 and y>=1 and y<=w){
x=h;
}
else if(x>h and y>=1 and y<=w){
x=1;
}
if(x>=1 and x<=h and y<1){
y=w;
}
else if(x>=1 and x<=h and y>w){
y=1;
}
return ;
}
void slove(){
int face=0;
int x=1,y=1;
for(int i=1;i<=n;i++){
// check(x,y);
if(a[x][y]=='.'){
a[x][y]='#';
shun(face,x,y);
check(x,y);
}
else if(a[x][y]=='#'){
a[x][y]='.';
ni(face,x,y);
check(x,y);
}
}
for(int i=1;i<=h;i++){
for(int j=1;j<=w;j++){
cout<<a[i][j];
}
cout<<endl;
}
}
int main() {
cin>>h>>w>>n;
for(int i=1;i<=h;i++){
for(int j=1;j<=w;j++){
a[i][j]='.';
}
}
slove();
}
C - Perfect Bus
贪心,分析
求区间和,边求变判断当前的区间和是否小于0
然后记下小于0里最小的哪个,即可
#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
typedef long long ll;
const int N = 1000+7;
int main(){
ll n;
cin>>n;
vector<ll> v(n+2);
for(int i=1;i<=n;i++){
cin>>v[i];
}
ll ans=0;
ll bns=0;
for(int i=1;i<=n;i++){
bns+=v[i];
if(bns<0){
ans=min(ans,bns);
}
}
ans=-ans;
cout<<ans+bns<<endl;
}
D - Synchronized Players
双搜
因为要求最短路,所以用到bfs。
开两个队列,同时bfs即可。
开一个map记录状态,或者开数组也行。map可能会mle
然后存下每次合法的状态,以及步数。
由于是bfs,所以每次步数都是上一次+1,很好转移
flag[x1][y1][x2][y2]
记录走到这个状态走了多少步。
最后遍历flag求最小值即可。
#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
typedef long long ll;
const int N = 1000+7;
int dx[]={-1,1,0,0};
int dy[]={0,0,-1,1};
int n;
char v[100][100];
int flag[61][61][61][61];
bool check(int x,int y){
return x>=0 and x<n and y>=0 and y<n and v[x][y]!='#';
}
void slove(int x1,int y1,int x2,int y2){ //求出两个p的坐标
ll cnt=0;
queue< pair<int,int> > q[2];
pair<int,int> t0={x1,y1},t1={x2,y2};
q[0].push(t0);
q[1].push(t1);
flag[x1][y1][x2][y2]=0;
while(!q[0].empty() and !q[1].empty() ){
t0=q[0].front();
t1=q[1].front();
q[0].pop();
q[1].pop();
for(int i=0;i<4;i++){
int xx1=t0.first+dx[i];
int yy1=t0.second+dy[i];
int xx2=t1.first+dx[i];
int yy2=t1.second+dy[i];
if(!check(xx1,yy1)){
xx1=t0.first;
yy1=t0.second;
}
if(!check(xx2,yy2)){
xx2=t1.first;
yy2=t1.second;
}
if(flag[xx1][yy1][xx2][yy2]==-1){
flag[xx1][yy1][xx2][yy2]=flag[t0.first][t0.second][t1.first][t1.second]+1;
q[0].push({xx1,yy1});
q[1].push({xx2,yy2});
}
}
}
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>v[i];
}
memset(flag,-1,sizeof flag);
int x1=0,y1=0,x2=0,y2=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(v[i][j]=='P'){
if(x1==0 and y1==0){
x1=i;
y1=j;
}
else{
x2=i;
y2=j;
}
}
}
}
slove(x1,y1,x2,y2);
int INF=0x7fffffff;
int ans=INF;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(flag[i][j][i][j]!=-1){
ans=min(ans,flag[i][j][i][j]);
}
}
}
if(ans==INF){
cout<<-1<<endl;
}
else {
cout<<ans<<endl;
}
}
E - Smooth Subsequence
权值线段树
对于这种没做过难题,我觉得从暴力做法入手,进而学习优化技巧。
暴力做法就是,求以a[i]为结尾的最长合法序列
而以 a[i]结尾的最长合法序列的前一项------a[j]
必然要在 [ a i − d , a i + d ] [\ a_i-d,a_i+d\ ] [ ai−d,ai+d ]区间内
所以我们只要再来一层循环,枚举 $j \in [1,i-1] ,以 a [ j ] ,以a[j] ,以a[j](a[j]\in[\ a_i-d,a_i+d\ ])$结尾的最长合法序列的长度
for (int i = 0; i < n; i++) {
for (int j = 0; j < i - 1; j++)
if (a[j] <= a[i] + d and a[j] >= a[i] - d)
dp[i] = max(dp[i], dp[j] + 1);
我们的优化思路,就从这里打开。
显然,要优化,不可能优化第一层循环,因为这是必需的时间复杂度O(n)的。
只能优化第二层循环——将n优化为logn。
第二层循环的目的是什么? 找到 1~i-1 内 [ a i − d , a i + d ] [\ a_i-d,a_i+d\ ] [ ai−d,ai+d ] 内最大的 dp[j]
通俗一点就是,在已经出现过的序列内的一个区间中找区间最大值。
————权值线段树
我们就以a[i] 作线段树的下标,然后 dp[i] 作对于的值。
就转化为了 普通线段树 求区间最值和单点修改最值。
#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr)
#define lson pos<<1
#define rson (pos<<1)|1
typedef long long ll;
const int N = 1e6+7;
struct Tree{
ll maxn;
}t[N<<2];
// 建树和更新结合,因为建的是空树所以没必要单独写
void update(ll pos,ll l,ll r,ll idx,ll val){
if(l==r){
t[pos].maxn=max(val,t[pos].maxn);
return;
}
ll mid=(l+r)>>1;
if(idx<=mid)update(lson,l,mid,idx,val);
if(idx>mid)update(rson,mid+1,r,idx,val);
t[pos].maxn=max(t[lson].maxn,t[rson].maxn);
}
//普通查询
ll query(ll pos,ll l,ll r,ll ql,ll qr){
if(ql<=l and qr>=r){
return t[pos].maxn;
}
ll mid=(l+r)>>1;
ll sum=0;
if(ql<=mid)sum=max(sum,query(lson,l,mid,ql,qr));
if(qr>mid)sum=max(sum,query(rson,mid+1,r,ql,qr));
return sum;
}
int main(){
ll n,d;
cin>>n>>d;
vector<ll> v(n);
ll maxn=0;//求序列最大值,作线段树的右端点
for(auto &i:v)cin>>i,maxn=max(maxn,i);
for(int i=0;i<n;i++){ //第一层循环
ll l=max(0ll,v[i]-d);
ll r=min(maxn,v[i]+d);
ll dp= query(1,1,maxn,l,r)+1;//区间查询
update(1,1,maxn,v[i],dp); //单点修改
}
cout<<query(1,1,maxn,1,maxn);//扫一整个区间
}
sum=max(sum,query(rson,mid+1,r,ql,qr));
return sum;
}
int main(){
ll n,d;
cin>>n>>d;
vector<ll> v(n);
ll maxn=0;//求序列最大值,作线段树的右端点
for(auto &i:v)cin>>i,maxn=max(maxn,i);
for(int i=0;i<n;i++){ //第一层循环
ll l=max(0ll,v[i]-d);
ll r=min(maxn,v[i]+d);
ll dp= query(1,1,maxn,l,r)+1;//区间查询
update(1,1,maxn,v[i],dp); //单点修改
}
cout<<query(1,1,maxn,1,maxn);//扫一整个区间
}