首页 > 其他分享 >AtCoder Beginner Contest 267

AtCoder Beginner Contest 267

时间:2022-09-05 20:11:55浏览次数:80  
标签:AtCoder return Beginner int sum 267 序列 保龄球 dp

A - Saturday

题意:
给定今天的日期,问到周六还有几天。

分析:
暴力判断即可。

代码:

int main(){

	cin>>s;
	
	if(s=="Monday") ans=5;
	if(s=="Tuesday") ans=4;
	if(s=="Wednesday") ans=3;
	if(s=="Thursday") ans=2;
	if(s=="Friday") ans=1;
	
	cout<<ans;

	return 0;
}

B - Split?

题意:
询问一个保龄球瓶阵是否是一个 split
split 的定义如下:

  • \(1\) 号保龄球被击倒
  • 存在两列不同的保龄球瓶,这两列保龄球瓶都没被全部击倒,且两列间存在一列保龄球瓶被全部击倒。

保龄球瓶的排列如下:

分析:
显然 \(1\) 号保龄球瓶没被击倒时一定输出 No
接下来就是暴力判断了,统计每列的保龄球瓶个数,然后枚举两列即可。

代码:

int get(int x){ // 返回 x 号保龄球瓶所在列
    if(x==7) return 1;
    if(x==4) return 2;
    if(x==2 || x==8) return 3;
    if(x==1 || x==5) return 4;
    if(x==9 || x==3) return 5;
    if(x==10) return 6;
}

int main(){

	cin>>s;
	s=" "+s;  
	
	if(s[1]=='1') cout<<"No";
	else{
	
	    vector<int> vec(7,0);
	
	    for(int i=1;i<=10;i++)
	        vec[get(i)]+=s[i]-'0';
	
	    bool flag=false;
	
	    for(int i=1;i<6;i++)
	        for(int j=i+1;j<=6;j++){
	        
	            if(vec[i]==0 || vec[j]==0) continue;
	            
	            bool flag2=false;
	            for(int k=i+1;k<j;k++)
	                if(vec[k]==0) flag2=true;
	
	            flag|=flag2;
	        }
	
	    cout<<(flag?"Yes":"No");
	}
	
	return 0;
}

C - Index × A(Continuous ver.)

题意:
给定一个长度为 \(n\) 的序列 \(A=(A_1,A_2,...,A_n)\),令 \(B\) 是 \(A\) 的一个长度为 \(m\) 的连续子序列,求 \(\sum\limits_{i=1}^m i*B_i\) 的最大值。

分析:
令 \(f(B)=\sum\limits_{i=1}^m i*B_i\),考虑求出 \(A\) 序列每一个长度为 \(m\) 的子序列的 \(f\) 值,取最大即可。
如果每次都暴力算过去的话,一共有 \(n-m+1\) 个子序列,每次计算复杂度为 \(O(m)\),而数据范围是 \(2*10^5\) 显然会超时。

我们发现,相邻两个子序列间有重复的部分,可以节省计算。

假设我们已经算出了 \(f(A_{i-m},...,A_{i-1})\) 的值(记为 \(x\)),那么我们该如何快速求出 \(f(A_{i-m+1},...,A_{i})\) 的值(记为 \(y\))呢?

不难发现,后者就是前者每个元素权重 \(-1\) 后再加上 \(m*A_i\),也就是 \(y=x-\sum\limits_{j=i-m}^{i-1}A_j+m*A_i\)。

而 \(\sum\limits_{j=i-m}^{i-1}A_j\) 我们可以通过前缀和预处理得到,所以此题得解,时间复杂度 \(O(n)\)。

代码:

int main(){

    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>arr[i],sum[i]=sum[i-1]+arr[i];

    i64 now=0;
    for(int i=1;i<=m;i++) now+=i*arr[i]; // 求出 f(A1,...,Am)

    i64 ans=now;
    for(int i=m+1;i<=n;i++){
    
		now-=sum[i-1]-sum[i-m-1];
        now+=m*arr[i];

        ans=max(ans,now);
    }

    cout<<ans;

    return 0;
}

D - Index × A(Not Continuous ver.)

题意:
给定一个长度为 \(n\) 的序列 \(A=(A_1,A_2,...,A_n)\),令 \(B\) 是 \(A\) 的一个长度为 \(m\) 的子序列,求 \(\sum\limits_{i=1}^m i*B_i\) 的最大值。
注:此题与上一题不同在子序列是否要求连续。

分析:
考虑采用动态规划。

设 \(dp_{i,j}\) 表示从前 \(i\) 个元素中选择 \(j\) 个能得到的最大值。

所以此题答案为 \(dp_{n,m}\)。

类似于背包问题,我们在当前有选或不选第 \(i\) 个数两种决策。

若不选择第 \(i\) 个数,则为 \(dp_{i-1,j}\)
若选择第 \(i\) 个数,则为 \(dp_{i-1,j-1}+j*A_i\)

所以状态转移方程就是:

\[dp_{i,j}=\max(dp_{i-1,j}\;,\;dp_{i-1,j-1}+j*A_i) \]

状态转移方程得出后,我们便不难编写此题代码了。

代码:

int main(){

    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>arr[i];

    // dp[i][j] 表示在前 i 个数中选 j 个得到的最大值

    dp[0][0]=0;
    dp[0][1]=-2e18; // 非法情况赋值为负无穷

    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++){

            if(j==0) dp[i][j]=0; // 一个都不取
            else if(j>i) dp[i][j]=-2e18; // 非法情况赋值为负无穷
            else dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+j*arr[i]);
        }

    cout<<dp[n][m];
    
    return 0;
}

此题还可以通过滚动数组优化空间,这里不再多说。

E - Erasing Vertices 2

题意:
给定一棵 \(n\) 个节点的树,每个节点有一个点权。

你需要进行 \(n\) 次操作,每次操作需要删掉任意一个点,删掉该点的代价为与该点相邻点的点权和,删去该点后与其相邻的边也会被删去。

定义 \(n\) 次操作的花费为这 \(n\) 次操作中代价的最大值,找到删掉整棵树的最小花费。

分析:
我们用 \(d_i\) 表示当前情况删掉第 \(i\) 个节点的代价。

容易发现决策的顺序对最后图的结构是没有影响的(\(n\) 个操作不管按什么顺序删最后图都会变为空),于是我们在操作的时候应尽量先删去 \(d\) 值小的节点,这样在当前是最优的,也不会使之后的操作变劣。

那么如何动态更新操作后每一个节点的 \(d\) 值并查找最小值呢?

朴素的做法是 \(O(n^2)\) 的,显然会超时,这里我们可以使用优先队列来优化,类似于 Dijkstra 算法,我们操作完对相邻节点的 \(d\) 值进行更新,然后只把更新过的 \(d\) 值放进优先队列里,这样子查找最小值的操作复杂度就变成了 \(O(\log n)\) 的,不会超时。

注:代码中 PLIpair<i64,int>pbpush_backmpmake_pair

代码:

int main(){

    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>arr[i];
    
    for(int i=1;i<=m;i++){
    
        int u,v;
        cin>>u>>v;

        G[u].pb(v);
        G[v].pb(u);
    }

    for(int i=1;i<=n;i++)
        for(auto v:G[i])
            d[i]+=arr[v];

    priority_queue<PLI,vector<PLI>,greater<PLI>> pq;
    
    for(int i=1;i<=n;i++) pq.push(mp(d[i],i));

    i64 ans=0;
    while(!pq.empty()){

        PLI now=pq.top(); pq.pop();

        if(vis[now.second]) continue;
        vis[now.second]=true;

        ans=max(ans,now.first);

        for(auto v:G[now.second]){ // 更新 d 值
            d[v]-=arr[now.second];
            pq.push(mp(d[v],v));
        }
    }

    cout<<ans;
    
    return 0;
}

标签:AtCoder,return,Beginner,int,sum,267,序列,保龄球,dp
From: https://www.cnblogs.com/xhgua/p/ABC267.html

相关文章

  • AtCoder Beginner Contest 267
    https://atcoder.jp/contests/abc267全部的AC代码:https://atcoder.jp/contests/abc267/submissions/me?f.Task=&f.LanguageName=&f.Status=AC&f.User=HinanawiTenshiE......
  • Atcoder Regular Contest 147
     AtcoderRegularContest147这是本蒟蒻第3次打的\(ARC\),赛时的时候\(B\)题调代码调崩了,\(wa\)了15发。所以来简略的写一下题解A:题目链接题目大意:略题目分析......
  • AtCoder Beginner Contest 267 解题报告
    A-Saturday题意:输入字符串代表周一至周五的某一天,输出这一天离周六还有多少天分析:映射一下,直接输入输出ac代码:#include<iostream>#include<algorithm>#inclu......
  • AtCoder Beginner Contest 267
    E-ErasingVertices2做法1观察可得:对于某个时刻,贪心删当前代价最小的点肯定是最优的。但是删一个点会减少相邻接的点的代价。然后就想到了堆,但是这个堆需要支持decre......
  • AtCoder Beginner Contest 267 E Erasing Vertices 2
    ErasingVertices2二分||贪心二分的做法就二分答案,然后检查一下能否删除,像拓扑一下跑一下就行#include<iostream>#include<cstdio>#include<algorithm>#includ......
  • ABC267总结
    比赛链接比赛情况AC:6/8题目分析A(语法入门)打表周一到周五即可B(基础算法)按照题意计算即可假如1号球没倒,则非法否则分别找最左和最右分别没倒的列,判断中间是否有一......
  • AtCoder ABC 259 F Select Edges
    题意:​ 给出一棵树,边带权,对于点i,最多保留d[i]条边,可以被分成连通块,请问边权和最大是多少分析:​ 因为可以被分成连通块,我们就可以对点i划分两种状态。第一种是点i不与父......
  • AtCoder Beginner Contest 266 G,H
    G考虑先放G和B,此时共有\(C_{G+B}^{B}\)种方案。然后选出\(k\)个G,在前面放上\(R\),共有\(C_{G}^{k}\)种方案。最后我们放剩下的\(R-K\)个R,考虑目前哪些区间内部可以放一段......
  • AtCoder Beginner Contest 266 一句话题解
    AandBsbt,不讲。C垃圾计算几何,问是不是一个凸包,搞份板子交就可以了。D简单dp,令\(f(i,j)\)表示第\(i\)个时间在第\(j\)个位置的最大价值,从上一个时间转移,可以......
  • NC235267 星球大战
    题目原题地址:星球大战题目编号:NC235267题目类型:map、list时间限制:C/C++2秒,其他语言4秒空间限制:C/C++262144K,其他语言524288K1.题目大意二维平面上n个坐标对应......