散题

2024牛客暑期多校训练营2 H Instructions Substring

时间:2024-07-18

原题:Instructions Substring

标签:前缀和、map

题意

初始在\((0,0)\)处,给一串操作,给目标位置,要求有多少操作能经过当前位置

也就是说在到达目标位置后的所有操作都算

思路

考虑用map维护前缀和,样例ASAWDD

可以构建:\(((-1,0),1),((-1,-1),2),((-2,-1),3),((-2,0),4),((-1,0),5),((0,0),6)\)

存储当前的位置前缀和和下标

然后开始枚举起点

比如从第二个A开始,当前处于 \((-2,-1)\) ,所以需要到达\((-1,0)\),在map中寻找后找到下标5

那么对答案的贡献就是 \(n-5+1\)

注意需要存储下标来寻找在当前位置往后的可行的点,并且要是最小的可行点才能保证答案最优

所以配合二分(lower_bound)

最后注意对 \((0,0)\) 特判,因为对于初始点,每次都能找到自己,但是自己是不需要\(n-i+1\)

只需要\(n-i\) ,所以也可以选择在 \(if (p != mp[target].end())\) 判定内加一个不等于自身的判定

代码

#include<bits/stdc++.h>
using namespace std;

#define YES "Yes"
#define NO "No"
#define int long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define rep(i,j,k) for(i=(j);i<(k);i++)
#define all(x) x.begin(),x.end()
#define vi vector<int>
typedef pair<int, int> pii;

const int N = 2e5 + 10;
int n;

string s;
int sw, ad;

void deal(pii & p,int i) {
    if (s[i] == 'W')p.second++;
    if (s[i] == 'S')p.second--;
    if (s[i] == 'A')p.first--;
    if (s[i] == 'D')p.first++;
}

void solve() {
    cin >> n >> sw >> ad;
    cin >> s;

	// 特判
    if ( sw==0 && ad==0) {
        cout << n * (n + 1) / 2 << endl;
        return;
    }

    map<pii, vi>mp;
    pii cur = { 0,0 };
    mp[cur].emplace_back(0);
    for (int i = 0; i < n; i++) {
        deal(cur, i);
        mp[cur].emplace_back(i + 1);
    }
    int ans = 0;

    cur = { 0,0 };
    for (int i = 0; i < n; i++) {
        //cur要到x,y位置
        pii target = { cur.first + sw,cur.second + ad };
        //先看有没有target
        if (mp.count(target)) {
            auto p = lower_bound(mp[target].begin(), mp[target].end(), i);
            // 再看看这个target是不是在当前的后面,如果在当前的前面,那是无法到达的
            // 在当前的前面也就是找不到的情况
                //if (*p == i) {
                    //ans += n - *p;
                //}
                //else {
                    ans += n - *p + 1;
                //}
        }
        deal(cur, i);
    }
    cout << ans;
}

signed main() {
    IOS;
    solve();
    return 0;
}

D - Grid Puzzle

时间:2024-07-20

原题:Codeforces Round 960 (Div. 2)

标签:贪心

题意

\(n\times n\) 的矩阵,给出第i行前 \(a_i\) 个是黑色

每次可以选择两种操作,把\(2 \times 2\)的格子涂成白色或者某一行涂成白色

思路

首先对于5以上的,不用说肯定是涂一行,因为五以上要三次,一行只需要2次,这是很直觉性的

现在讨论5以下,对于小于等于2的,进行涂色,同时要处理下一行的格子数

如果大于2,直接答案+1

比如当前是1 3,处理了1后,第二行由于也被涂色,会变成1,也被涂色,所以答案+2

如果当前是3 3,遇到第一行,答案+1,遇到第二行,答案+1,答案不变

实质上这是贪心的做法,不过要发现2的特殊性,然后多尝试

代码

#include <bits/stdc++.h>
using namespace std;

#define YES "Yes"
#define NO "No"
#define int long long
#define ull unsigned long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define rep(i, j, k) for (i = (j); i < (k); i++)
#define all(x) x.begin(), x.end()
#define vi vector<int>
typedef pair<int, int> pii;
  
const int MOD = 1000000007;
const int N = 2e5 + 10;
int ii, jj, kk;
  
int n;
// 涂色的格子
int arr[N][6];
// 输入的数据
int a[N];
void solve() {
    cin >> n;
    int temp;
    int ans = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < 5; j++) {
            arr[i][j] = 0;
        }
    }
    rep(ii, 0, n) {
        cin >> a[ii];
        // 只有当一行的格子数小于5时才需要考虑是否用1
        if (a[ii] < 5) {
            rep(jj, 0, a[ii]) {
                arr[ii][jj] = 1;
            }
        } else {
            a[ii] = 0;
            ans++;
        }
    }
  
    for (int i = 0; i < n; i++) {
        if (a[i] == 0)
            continue;
        if (a[i] > 2)
            ans++;
        else
            for (int j = 0; j < 4; j++) {
                if (arr[i][j] == 1) {
                    arr[i][j] = 0;
                    arr[i][j + 1] = 0;
                    if (arr[i + 1][j] == 1) {
                        arr[i + 1][j] = 0;
                        a[i + 1]--;
                    }
                    if (arr[i + 1][j + 1] == 1) {
                        arr[i + 1][j + 1] = 0;
                        a[i + 1]--;
                    }
                    ans++;
                }
            }
    }
    cout << ans << endl;
}
  
signed main() {
    IOS;
    int t = 1;
    cin >> t;
    while (t--) {
        // cout << "Case #" << t + 1 << ": ";
        solve();
    }
    return 0;
}

星星

时间:2024-07-19

原题:2024“钉耙编程”中国大学生算法设计超级联赛(1)

标签:二维dp

题意

每轮可以选择获得0到5颗星星,但是每轮每个星星的代价不同

要求n轮后正好拥有k颗星星,求最小代价

思路

直接就是一手2维dp,就是初始化怎么搞

代码

#include <bits/stdc++.h>
using namespace std;

#define YES "Yes"
#define NO "No"
#define int long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define rep(i, j, k) for (i = (j); i < (k); i++)
#define all(x) x.begin(), x.end()
#define vi vector<int>
#define pii pair<int, int>

const int MOD = 1000000007;
const int N = 1010;
int ii, jj, kk, n, k;

int a[N], b[N], c[N], d[N];

// 表示当前回合已经获得的糖数
int dp[N][4 * N] = {0};
void solve() {
    cin >> n >> k;
    rep(ii, 1, n + 1) {
        cin >> a[ii] >> b[ii] >> c[ii] >> d[ii];
    }

	// 考虑到dp[i][j]依赖于上一层的j-4到j,层层依赖,所以初始将第0层修改为max
    for (int i = 0; i <= k; i++)
        dp[0][i] = 1e18;
    // 对于0层的0置0
    dp[0][0] = 0;

    for (int i = 1; i <= n; i++) {
        dp[i][0] = 0;
        for (int j = 1; j <= k; j++) {
            dp[i][j] = dp[i - 1][j];
            if (j > 0)
                dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + a[i]);
            if (j > 1)
                dp[i][j] = min(dp[i][j], dp[i - 1][j - 2] + b[i]);
            if (j > 2)
                dp[i][j] = min(dp[i][j], dp[i - 1][j - 3] + c[i]);
            if (j > 3)
                dp[i][j] = min(dp[i][j], dp[i - 1][j - 4] + d[i]);
        }
    }
    cout << dp[n][k] << endl;
}
signed main() {
    IOS;
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}
posted @ 2024-07-23 10:07  lulaalu  阅读(19)  评论(0编辑  收藏  举报