AtCoder Beginner Contest 339

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\ ] [ aid,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\ ] [ aid,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);//扫一整个区间

}




  • 19
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

louisdlee.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值