AtCoder Beginner Contest 267 (A~D)

A. Saturday

题意:

告诉你今天星期几,问距离周六还有几天。

思路:

直接输出即可。

代码如下:

#include 
#include 
using namespace std;

int main()
{
    string s;
    cin >> s;

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

    return 0;
}

B. Split?

题意:

有一个保龄球图,保龄球编号从 1 到 10,其分布如下图所示。

将图中两条虚线的每个部分称为一列。
AtCoder Beginner Contest 267 (A~D)_第1张图片

给定一个长度为 10 的 01串,一一对应每个保龄球,其中 0 表示被击倒,1 表示未被击倒。

问给定的序列是否满足:

  • 第一个保龄球被击倒

  • 存在两列都有未被击倒的保龄球,并且中间存在一列,其中的保龄球全部被击倒。

满足输出 Yes ,不满足则输出 No .

思路:

先将字符转化为数字存放在 a[] 中,再进行判断:要满足题意,那么 1 号必须为 0 .

接下来逐个分析,要求至少有一列都为 0 ,且除了该列之外每一列都至少有一个 1 .

代码如下:

#include 
#include 
#include 
#define ll long long 
using namespace std;

int main()
{
    string s;
    cin >> s;
    s = ' ' + s;

    int a[20];
    for (int i = 1; i <= 10; i++)
        a[i] = s[i] - '0';

    if (s[1] == '0'){
        int sum = 0;
        for (int i = 1; i <= 10; i++){
            sum += a[i];
        }
        if (sum == 0){
            cout << "No" << endl;
            return 0;
        }

        if (a[3] + a[9] == 0){
            cout << "Yes" << endl;
            return 0;
        }
        if (a[2] + a[8] == 0){
            cout << "Yes" << endl;
            return 0;
        }
        if (a[5] == 0){
            cout << "Yes" << endl;
            return 0;
        }
        if (a[7] == 1 && a[4] == 0){
            cout << "Yes" << endl;
            return 0;
        }
        if (a[10] == 1 && a[6] == 0){
            cout << "Yes" << endl;
            return 0;
        }

        cout << "No" << endl;
    }
    else puts("No");

    return 0;
}

C. Index × A (Continuous ver.)

题意:

给定一个长度为 n 的数组 A,要求找到一段长度为 m 的连续子序列 B,使得 B 中每个数与其序号的乘积之和最大,输出最大值。

思路:

很容易想到用双指针来做,维护一个长度为 m 的滑动窗口,每次移动,都是将后一个数加入并将第一个数移出,还要重新计算当前窗口内的数值,如果每次都这样操作一遍的话,很容易时间复杂度过高导致超时。

我们发现每次移动前面都减少了一个从 lr 的区间和,由此想到可以用一个前缀和数组来维护区间和。

代码如下:

#include 
#include 
#include 
#define ll long long 
using namespace std;
const int N = 2e5 + 10;

ll a[N];
ll s[N];

int main()
{
    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= n; i++){
        cin >> a[i];
        s[i] = s[i - 1] + a[i]; //前缀和
    }

    ll res = 0;
    for (int i = 1; i <= m; i++) //初始窗口,前m个数
        res += a[i] * i;

    //双指针
    ll ans = res;
    for (int i = m + 1; i <= n; i++){
        int l = i - m + 1, r = i;
        res += m * a[r] - (s[r - 1] - s[l - 2]);
        ans = max(ans, res);
    }

    cout << ans << endl;

    return 0;
}

D.Index × A (Not Continuous ver.)

题意:

与上题类似,只不过将连续子序列改成了子序列(可以不连续)。

给定一个长度为 n 的数组 A,要求找到一段长度为 m 的子序列 B,使得 B 中每个数与其序号的乘积之和最大,输出最大值。

思路:

n 的范围较大,考虑使用 dp 来做。

定义 f[i][j] 表示前 i 个数中选了 j 个数。

得到状态转移方程为:f[i][j] = max(f[i - 1][j - 1] + a[i] * j, f[i - 1][j]) .

代码如下:

#include 
#include 
#include 
#define ll long long
using namespace std;
const int N = 2010;

int a[N];
ll f[N][N]; //表示前i个数选了j个数

int main()
{
    int n, m;
    cin >> n >> m;

    memset(f, -0x3f, sizeof(f));
    
    for (int i = 1; i <= n; i++){
        cin >> a[i];
        f[i][0] = 0;
    }

    f[0][0] = 0; //初始化
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m && j <= i; j++){
            f[i][j] = max(f[i - 1][j - 1] + a[i] * j, f[i - 1][j]);
        }
    }

    cout << f[n][m] << endl;

    return 0;
}

你可能感兴趣的:(AtCoder,c++,算法,双指针,前缀和,dp)