• 2019UMS培训day3解题报告


    T1:赌徒

    链接:https://www.luogu.org/problem/T92080

    $sol1:$设$f[i][j]$表示目前得到了$x$分,此时骰子面朝$i$,那么$f[i+k][k]=max(f[i][j]+1,f[i+k][k])$。

    先预处理之后直接查询即可。

    总复杂度$O(n)$。

    $sol2:$考虑最小步数,则先去用$5,6$使之后次数更小,然后用其他面去凑,注意$7$的特判。

    代码$(sol1)$:

    #include <bits/stdc++.h>
    const int MAXN = 1000001;
    const int INF = 1 << 30;
    using namespace std;
    int f[MAXN][10], t, n;
    void init() {
        memset(f, 127, sizeof(f));
        f[0][1] = 0;
        for(int i = 0; i <= MAXN; i++) {
            for(int j = 1; j <= 6; j++) {
                for(int k = 1; k <= 6; k++) {
                    if(j != k && j + k != 7)
                        f[i + k][k] = min(f[i + k][k], f[i][j] + 1); 
                }
            }
        }
    }
    int dp(int x) {
        int ans = INF;
        for(int i = 1; i <= 6; i++)
            ans = min(ans, f[x][i]);
        return ans;
    }
    int main() {
        cin >> t;
        init();
        while(t--) {
            cin >> n;
            cout << dp(n) << endl;
        }
        return 0;
    } 

    T2:盒子

    链接:https://www.luogu.org/problem/T92083

    $sol:$设$A[l][r]$表示人处于先手,在$l,r$的范围内进行选择,得到的最优答案,反之$B[l][r]$表示人处于后手......

    那么一个人得到的最大的分即可以表示为$f[l][r]=max(A[l+1][r]+a[l],B[l][r-1]+a[r])$。

    对于$B[l][r]$,因为是后手,所以可以直接求出。

    注意要使用记忆化搜索。

    最后答案即为$A(1,n)-B(1,n)$。

    代码:

    #include <bits/stdc++.h>
    const int MAXN = 1050;
    using namespace std;
    int t, n, a[MAXN], sum[MAXN], f[MAXN][MAXN];
    int A(int l, int r);
    int B(int l, int r);
    int B(int l, int r) { return sum[r] - sum[l - 1] - A(l, r); }
    int A(int l, int r) {
        if(l == r)
            return a[l];
        if(f[l][r])
            return f[l][r];
        f[l][r] = max(B(l + 1, r) + a[l], B(l, r - 1) + a[r]);
        return f[l][r];
    }
    void init() {
        memset(f, 0, sizeof(f));
    }
    int main() {
        cin >> t;
        for(int i = 1; i <= t; i++) {
            cin >> n;
            for(int i = 1; i <= n; i++) {
                cin >> a[i];
                sum[i] = sum[i - 1] + a[i];
            }
            cout << A(1, n) - B(1, n) << endl;
            init();
        }
        return 0;
    }

    T3:楼房修建

    链接:https://www.luogu.org/problem/P4198

    $sol1:$这道题本质就是求区间内的最长斜率上升子序列,考虑用线段树维护。

    线段树里维护该区间的斜率最大值,以及仅该区间的最长斜率上升子序列。

    斜率的$pushup$很简单,对于后者,考虑到修改一个点,对其左侧的答案没有影响,那我们可以去递归其右子树。

    对于右子树,把当前区间从中划分为两部分,若左边区间的斜率最大值都比当前传进来的斜率要小,那么只用去递归右子树。

    否则答案即为递归左子树的答案加上$ans[p]-ans[ls]$。

    做法很玄学。

    $sol2:$分块(咕)

    代码$(sol1)$:

    #include <bits/stdc++.h>
    #define ls p << 1
    #define rs p << 1 | 1
    const int MAXN = 100050;
    using namespace std;
    struct node {
        double Max;
    }a[MAXN << 2];
    int n, m, x, y, ans[MAXN << 2];
    int read() {
        int x = 0;
        bool sign = false;
        char alpha = 0;
        while(!isdigit(alpha)) {
            sign |= alpha == '-';
            alpha = getchar();
        }
        while(isdigit(alpha)) {
            x = (x << 1) + (x << 3) + (alpha ^ 48);
            alpha = getchar();
        }
        return sign ? -x : x;
    }
    struct Segment_Tree {
        void push_up(int p) { a[p].Max = max(a[ls].Max, a[rs].Max); }
        int query(int p, int l, int r, double k) {
            if(a[p].Max <= k)
                return 0;
            if(l == r)
                return a[p].Max > k;
            int mid = (l + r) >> 1;
            if(a[ls].Max <= k)
                return query(rs, mid + 1, r, k);
            else 
                return query(ls, l, mid, k) + ans[p] - ans[ls];         
        }
        void update(int p, int l, int r, int x, double k) {
            if(l == r) {
                ans[p] = 1;
                a[p].Max = k;
                return ; 
            }
            int mid = (l + r) >> 1;
            if(x <= mid)
                update(ls, l, mid, x, k);
            if(x > mid) 
                update(rs, mid + 1, r, x, k);
            push_up(p);
            ans[p] = ans[ls] + query(rs, mid + 1, r, a[ls].Max);
        }
    }Tree;
    int main() {
        n = read();
        m = read();
        for(int i = 1, x, y;i <= m; i++) {
            x = read();
            y = read();
            Tree.update(1, 1, n, x, (double)y / x);
            cout << ans[1] << endl;
        }
        return 0;
    }

    T4:弹飞绵羊

    $sol1:$分块

    (咕)

  • 相关阅读:
    绿色版Notepad++ 加右键带图标菜单
    C#中string和StringBuilder的区别
    C#中string和String的区别
    C#中is和as的区别
    C#中抽象类(abstract)和接口(interface)的相同点与区别
    c++串口通信实例
    vs2017常用快捷键
    Qt编译opencv找不到头文件
    Qt常用快捷键
    二维数组和指针
  • 原文地址:https://www.cnblogs.com/BeyondLimits/p/11311664.html
Copyright © 2020-2023  润新知